安卓Lifecycle+ViewModel+LiveData+Mvp+Dagger2完美搭建

先说几个概念
一,Lifecycle
这个玩意是用来管理监听Actitivy生命周期的一个东西,之前我们可能会写一个生命周期的回调来做这个事情,比如在对应的生命周期中回调P层来达到监听的效果,但如果我们写个自定义控件也要监听呢,回头又有一个地方需要监听呢?当然写回调可以到达目的,但是很不好管理.所以Google推出这个东西,现在高版本的supper包已经默认支持这个,所以不需要额外引入,需要监听的类只要实现LifecycleObserver这个接口,并添加到监听中就可以很好的管理.用法不多说

二,ViewModel+LiveData
这个东西呢有好处,也有一定的弊端,个人认为.首先呢它可以很好的处理生命周期的问题,数据回调时ui组件生命周期处于不活跃状态也不会出现空指针的问题,课能这个是最大的好处,然后一点该组件可以及时的收到数据变化的回调,我们不必要管数据是如何变化的,那里变化的,怎们变化的,只要它变化了我们处理即可.弊端呢,我觉的单纯的一个onChange方法很难满足我们的需求,我们无法做一些其他的回调,比如网络错误,服务器自定义的一些返回码等这种状况,必须做一些封装处理.但我认为该组件的利大于弊,值得引入项目.

三,Mvp
mvp的概念说烂了,各种各样,根据自己去搭建适合自己项目的mvp,并非要写一堆契约类,写一堆model,仁者见仁,智者见智.之前的项目我会为M,V,P三层都写一个扩展接口,然后写个契约类.标准的Google结构,但是实际项目中,P层作为一个业务逻辑层,很少会有扩展或者复用的情况.所以我觉得P层完全可以作为activity的一个逻辑抽离,不必要写的太复杂.反而model层作为数据获取的一层,会有很多的变化,我们必须要做好扩展,View层同理.

四,Dagger2
类似Spring中依赖倒置的一个东西吧,好处是做到了很强的解耦,并做到了很好的统一管理.坏处也有,你写的代码,可能换个人会看不懂,即使他会Dagger2!为神们呢?其实dagger很简单,无非inject,model这些东西,理解了概念就会写了,生命周期也好理解,而且也不会用的太多太过复杂.他的难点是神们?是如何去组合model,去写Component,你总不能每个类,每个需要注入的写一个文件吧,怎么写就看你个人理解,所以我认为这个是难点!

好,概念介绍完了,来看我们的框架

首先按照常规搭建一个baseActivity(下面是完整的类)

这个地方主要做的就是生命周期的处理,我们写一个lifecycleObservers 来保存所有的需要回调的对象,并在初始化的时候讲P保存到集合中,然后其他的都是一些常规操作,比如显示toast,状态栏的相关设置,我们将这些抽取到一个借口中IBaseView.

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements IBaseView {

    @Inject
    public P mPresenter;

    public Context mContext;

    private LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    private ArrayList<LifecycleCallback> lifecycleObservers = new ArrayList<>();
    //键盘的状态(弹出/收回)
    private boolean isKeyBoardShow;
    private Unbinder bind;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initDagger();
        mContext = this;
        mPresenter.setView(this);
        doBeforeSetcontentView();
        setContentView(getLayoutRes());
        bind = ButterKnife.bind(this);
        addLifecycleObserver(mPresenter);
        BusManager.getBus().register(this);
        initView();
    }

    /**
     * 设置layout前配置
     */
    private void doBeforeSetcontentView() {
        Window window = getWindow();
        window.setBackgroundDrawableResource(R.color.colorEEEEEE);
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
        // 无标题
        if (getSupportActionBar() != null) {
            getSupportActionBar().hide();
        }
        // 默认着色状态栏
        setStatusBarColor();
        SoftKeyBoardListener.setListener((Activity) mContext, new SoftKeyBoardListener.OnSoftKeyBoardChangeListener() {
            @Override
            public void keyBoardShow(int height) {
                isKeyBoardShow = true;
            }

            @Override
            public void keyBoardHide(int height) {
                isKeyBoardShow = false;
            }
        });
    }


    public boolean isKeyBoardShow() {
        return isKeyBoardShow;
    }

    public void setKeyBoardShow(boolean keyBoardShow) {
        isKeyBoardShow = keyBoardShow;
    }

    /**
     * 获取布局文件
     *
     * @return
     */
    public abstract int getLayoutRes();


    /**
     * 子类的后续操作
     */
    public abstract void initView();


    /**
     * 初始化Dagger
     */
    public abstract void initDagger();


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (lifecycleRegistry != null) {
            removeObservers();
        }
        if (bind != null) {
            bind.unbind();
        }
    }

    /**
     * 添加所有的需要监听的类到监听器中
     */
    public void addLifecycleObserver(LifecycleCallback lifecycleCallback) {
        if (lifecycleCallback == null) return;
        if (!lifecycleObservers.contains(lifecycleCallback)) {
            lifecycleObservers.add(lifecycleCallback);
        }
        lifecycleRegistry.addObserver(lifecycleCallback);
    }

    /**
     * 移除所有的生命周期监听器
     */
    private void removeObservers() {
        for (int i = 0; i < lifecycleObservers.size(); i++) {
            LifecycleCallback lifecycleObserver = lifecycleObservers.get(i);
            lifecycleRegistry.removeObserver(lifecycleObserver);
        }
        lifecycleObservers.clear();
    }

    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }

    @Override
    public void setStatusBarTransparent() {
        StatusBarUtil.setTransparent(this);
    }

    @Override
    public void setStatusBarColor() {
        StatusBarUtil.setColor(this, getResources().getColor(R.color.colorPrimary));
    }

    @Override
    public void setStatusBarColor(int color) {
        StatusBarUtil.setColor(this, color);
    }

    @Override
    public void setStatusBarColor(int color, int alpha) {
        StatusBarUtil.setColor(this, color, alpha);
    }

    @Override
    public void showShortToast(String content) {
        ToastUtil.showLong(this, content);
    }

    @Override
    public void showShortToast(int id) {
        ToastUtil.showLong(this, id);
    }

    @Override
    public void showLongToast(String content) {
        ToastUtil.showLong(this, content);
    }

    @Override
    public void showLongToast(int id) {
        ToastUtil.showLong(this, id);
    }

    @Override
    public void finsActivity() {
        ActivityManager.getInstance().finishActivity(this);
    }
}

好,然后看一下BasePersenter(下面是完整的类)
我自己写了一个LifecycleCallback继承LifecycleObserver ,将生命周期的回调统一写到了该借口中,之后那个类像被监听只需要是相爱借口即可,也不用去写生命周期的回调.
P层我们setView方法引用View

public class BasePresenter<V extends IBaseView> implements LifecycleCallback {


    public V view;

    public BasePresenter() {

    }

    public void setView(V view) {
        this.view = view;
    }

    public String TAG = getClass().getSimpleName();

    @Override
    public void onStart() {
        Log.e(TAG, "onStart");
    }

    @Override
    public void onCreat() {
        Log.e(TAG, "onCreat");
    }

    @Override
    public void onResume() {
        Log.e(TAG, "onResume");
    }


    @Override
    public void onPause() {
        Log.e(TAG, "onPause");
    }

    @Override
    public void onStop() {
        Log.e(TAG, "onStop");
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");
    }

}
public interface LifecycleCallback extends LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart();

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreat();

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onResume();


    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    void onPause();

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onStop();

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy();
}

之后看一下viewModel+LiveData的处理,
我写了一个BaseViewModel,在初始化的时候,创建一个CommonLiveData,这个就是要进行操作变化的LiveData,CommonLiveData我做了一些简单的扩展来处理一些其他事情,比如网络错误等.
BaseErrorHandling 是个神们呢?我理解的可能是我们可能对错误码会做一些全局的处理,比如多端登录啊,数据处理啊等,但是又有可能部分接口不需要处理,比如有些接口不需要验证用户是否登录神们的,而且每个项目的处理方式是不通的,所以我将其抽取到上层来让用户自定义这个错误处理器,只需要在ViewModel的构造中窜入一个继承BaseErrorHandling的自定义Handing即可

public abstract class BaseViewModel<T> extends ViewModel implements IBaseViewModel<T> {


    public CommonLiveData<T> liveData;

    public BaseViewModel(BaseErrorHandling errorHandling) {
        liveData = new CommonLiveData<>();
        liveData.setErrorHandling(errorHandling);
    }

    @Override
    public CommonLiveData<T> getData() {
        return liveData;
    }
}
public class ErrorHandling extends BaseErrorHandling {



    public static int TOKENERROR = 401; //参数校验未通过


    public boolean handing(int errorCode, String msg) {
        return true;
    }
}
@Module
public class ViewModelModel {

    @Provides
    UserViewModel provideUserViewModel() {
        return new UserViewModel(new UserHttpRepertory(),new ErrorHandling());
    }


}

然后来看一下CommonLiveData这个东西
我们重写一下setValue,postValue方法,对空值做一下统一的判断,当然也可以验证其他你乐意的东西,
另外我增加了一个setValue(int errorCode, String msg)的重载方法来处理获取数据出现问题的情况,统一的处理errorHandling已经做过,暴露一个回调commonLiveDataCall来交给业务代码来处理,这样就可以处理网络错误啊,数据库错误啊,服务器自定义错误啊的种种情况.

public class CommonLiveData<T> extends MutableLiveData<T> {

   private CommonLiveDataCall commonLiveDataCall;


   private BaseErrorHandling errorHandling;

   public void setErrorHandling(BaseErrorHandling errorHandling) {
       this.errorHandling = errorHandling;
   }

   @Override
   public void postValue(T value) {
       //获取数据失败
       if (value == null) {
           setValue(BaseErrorHandling.NULLERROR, "data can't be null");
       } else {
           super.postValue(value);
       }
   }

   @Override
   public void setValue(T value) {
       //获取数据失败
       if (value == null) {
           setValue(BaseErrorHandling.NULLERROR, "data can't be null");
       } else {
           super.setValue(value);
       }
   }


   public CommonLiveData<T> setCommonLiveDataCall(CommonLiveDataCall commonLiveDataCall) {
       this.commonLiveDataCall = commonLiveDataCall;
       return this;
   }

   public void setValue(int errorCode, String msg) {
       boolean handing = errorHandling.handing(errorCode, msg);
       if (!handing) return;
       commonLiveDataCall.dataError(errorCode, msg);
   }

   public interface CommonLiveDataCall {

       void dataError(int errorCode, String msg);
   }


}

用法如下
这是一个常规的P层获取列表的操作

  @Override
    public void onCreat() {
        view.showLoading();
        userViewModel.getData().
                setCommonLiveDataCall(new CommonLiveData.CommonLiveDataCall() {
                    @Override
                    public void dataError(int errorCode, String msg) {
                      //说明获取数据失败了,我们切不管具体错误码,通知view层显示错误的界面
                        view.showError();
                    }
                }).
                observe(view, new Observer<ArrayList<User>>() {
                    @Override
                    public void onChanged(@Nullable ArrayList<User> users) {
                        if (users.size() == 0) {
                            //显示空界面
                            view.showEmpty();
                            return;
                        }
                        view.setData(users);
                    }
                });
        changeUser();
    }

我们再看viewModel层,该层对livedata做了一个包装,官方建议数据的改变我们交给Repertory(仓库)来做,来达到方便扩展的目的,我也这么建议,而且很有必要
如下是我们的UserViewModel,在构造ViewModel的同时我们将IUserRepertory 仓库的实现类传入

public class UserViewModel extends BaseViewModel<ArrayList<User>> {


    IUserRepertory userRepertory;

    @Inject
    public UserViewModel(IUserRepertory userRepertory , BaseErrorHandling errorHandling) {
        super(errorHandling);
        this.userRepertory = userRepertory;
    }

    public void loadData() {
        userRepertory.getUser(liveData);
    }
}

下面模拟一个网络数据的获取

public class UserHttpRepertory extends HttpRepertory implements IUserRepertory {

    @Inject
    public UserHttpRepertory() {

    }

    @Override
    public void getUser(final CommonLiveData<ArrayList<User>> liveData) {

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                ArrayList<User> users = new ArrayList<>();
                for (int i = 0; i < 20; i++) {
                    Random random = new Random();
                    User user = new User();
                    user.name = String.valueOf(random.nextInt(10000000));
                    user.phone = String.valueOf(random.nextInt(10000000));
                    user.userId = String.valueOf(random.nextInt(10000000));
                    users.add(user);
                }

                // liveData.setValue(BaseErrorHandling.HTTPERROR,"网络错误");
                liveData.setValue(users);
            }
        }, 1000);
    }
}

之后如果你要换数据获取的方式,只需要更改dagger2配置文件,如下
我们就将这个数据的获取方式换成了数据库获取,数据库的获取方式自己具体实现,P层,view层,甚至viewmodel不需要做任何的变更

@Module
public class ViewModelModel {

    @Provides
    UserViewModel provideUserViewModel() {
        return new UserViewModel(new UserDaoRepertory(),new ErrorHandling());
    }


}

好了,大体的一个实录就是这样的,我们还可以做更多的封装,下面是几点建议
1,每个数据结构对应一个viewModel,每个viewModel只对应一个数据结构.
2,每个Repertory可以有多个数据结构的改变,具体自己考虑,比方user相关的数据获取可以放到一个仓库,goods相关的可以放到一个仓库等
3,关于dagger2,我建议可以将所有的viewmode写到一个model配置文件中,改的时候也好找,其他的不涉及到第三方的构造或者接口的直接构造加个injiect就可以了,没必要单独出来写个model,所以总之model我建议分类放(viewmodel,adapter等等)
4,Component 注入器更具自己考虑写,我建议更具ui界面分开写,方便管理,毕竟需要注入的东西课能都不一样,当然写到一个里面也可以,便于封装.

差不多就这些内容,有什么不足的请多多指教.

demo地址
https://github.com/wxxewx/LiveDataDemo.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容