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

先說幾個概念
一,Lifecycle
這個玩意是用來管理監(jiān)聽Actitivy生命周期的一個東西,之前我們可能會寫一個生命周期的回調(diào)來做這個事情,比如在對應(yīng)的生命周期中回調(diào)P層來達(dá)到監(jiān)聽的效果,但如果我們寫個自定義控件也要監(jiān)聽呢,回頭又有一個地方需要監(jiān)聽呢?當(dāng)然寫回調(diào)可以到達(dá)目的,但是很不好管理.所以Google推出這個東西,現(xiàn)在高版本的supper包已經(jīng)默認(rèn)支持這個,所以不需要額外引入,需要監(jiān)聽的類只要實(shí)現(xiàn)LifecycleObserver這個接口,并添加到監(jiān)聽中就可以很好的管理.用法不多說

二,ViewModel+LiveData
這個東西呢有好處,也有一定的弊端,個人認(rèn)為.首先呢它可以很好的處理生命周期的問題,數(shù)據(jù)回調(diào)時ui組件生命周期處于不活躍狀態(tài)也不會出現(xiàn)空指針的問題,課能這個是最大的好處,然后一點(diǎn)該組件可以及時的收到數(shù)據(jù)變化的回調(diào),我們不必要管數(shù)據(jù)是如何變化的,那里變化的,怎們變化的,只要它變化了我們處理即可.弊端呢,我覺的單純的一個onChange方法很難滿足我們的需求,我們無法做一些其他的回調(diào),比如網(wǎng)絡(luò)錯誤,服務(wù)器自定義的一些返回碼等這種狀況,必須做一些封裝處理.但我認(rèn)為該組件的利大于弊,值得引入項目.

三,Mvp
mvp的概念說爛了,各種各樣,根據(jù)自己去搭建適合自己項目的mvp,并非要寫一堆契約類,寫一堆model,仁者見仁,智者見智.之前的項目我會為M,V,P三層都寫一個擴(kuò)展接口,然后寫個契約類.標(biāo)準(zhǔn)的Google結(jié)構(gòu),但是實(shí)際項目中,P層作為一個業(yè)務(wù)邏輯層,很少會有擴(kuò)展或者復(fù)用的情況.所以我覺得P層完全可以作為activity的一個邏輯抽離,不必要寫的太復(fù)雜.反而model層作為數(shù)據(jù)獲取的一層,會有很多的變化,我們必須要做好擴(kuò)展,View層同理.

四,Dagger2
類似Spring中依賴倒置的一個東西吧,好處是做到了很強(qiáng)的解耦,并做到了很好的統(tǒng)一管理.壞處也有,你寫的代碼,可能換個人會看不懂,即使他會Dagger2!為神們呢?其實(shí)dagger很簡單,無非inject,model這些東西,理解了概念就會寫了,生命周期也好理解,而且也不會用的太多太過復(fù)雜.他的難點(diǎn)是神們?是如何去組合model,去寫Component,你總不能每個類,每個需要注入的寫一個文件吧,怎么寫就看你個人理解,所以我認(rèn)為這個是難點(diǎn)!

好,概念介紹完了,來看我們的框架

首先按照常規(guī)搭建一個baseActivity(下面是完整的類)

這個地方主要做的就是生命周期的處理,我們寫一個lifecycleObservers 來保存所有的需要回調(diào)的對象,并在初始化的時候講P保存到集合中,然后其他的都是一些常規(guī)操作,比如顯示toast,狀態(tài)欄的相關(guān)設(shè)置,我們將這些抽取到一個借口中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<>();
    //鍵盤的狀態(tài)(彈出/收回)
    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();
    }

    /**
     * 設(shè)置layout前配置
     */
    private void doBeforeSetcontentView() {
        Window window = getWindow();
        window.setBackgroundDrawableResource(R.color.colorEEEEEE);
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
        // 無標(biāo)題
        if (getSupportActionBar() != null) {
            getSupportActionBar().hide();
        }
        // 默認(rèn)著色狀態(tài)欄
        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();


    /**
     * 子類的后續(xù)操作
     */
    public abstract void initView();


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


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

    /**
     * 添加所有的需要監(jiān)聽的類到監(jiān)聽器中
     */
    public void addLifecycleObserver(LifecycleCallback lifecycleCallback) {
        if (lifecycleCallback == null) return;
        if (!lifecycleObservers.contains(lifecycleCallback)) {
            lifecycleObservers.add(lifecycleCallback);
        }
        lifecycleRegistry.addObserver(lifecycleCallback);
    }

    /**
     * 移除所有的生命周期監(jiān)聽器
     */
    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 ,將生命周期的回調(diào)統(tǒng)一寫到了該借口中,之后那個類像被監(jiān)聽只需要是相愛借口即可,也不用去寫生命周期的回調(diào).
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,在初始化的時候,創(chuàng)建一個CommonLiveData,這個就是要進(jìn)行操作變化的LiveData,CommonLiveData我做了一些簡單的擴(kuò)展來處理一些其他事情,比如網(wǎng)絡(luò)錯誤等.
BaseErrorHandling 是個神們呢?我理解的可能是我們可能對錯誤碼會做一些全局的處理,比如多端登錄啊,數(shù)據(jù)處理啊等,但是又有可能部分接口不需要處理,比如有些接口不需要驗證用戶是否登錄神們的,而且每個項目的處理方式是不通的,所以我將其抽取到上層來讓用戶自定義這個錯誤處理器,只需要在ViewModel的構(gòu)造中竄入一個繼承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; //參數(shù)校驗未通過


    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方法,對空值做一下統(tǒng)一的判斷,當(dāng)然也可以驗證其他你樂意的東西,
另外我增加了一個setValue(int errorCode, String msg)的重載方法來處理獲取數(shù)據(jù)出現(xiàn)問題的情況,統(tǒng)一的處理errorHandling已經(jīng)做過,暴露一個回調(diào)commonLiveDataCall來交給業(yè)務(wù)代碼來處理,這樣就可以處理網(wǎng)絡(luò)錯誤啊,數(shù)據(jù)庫錯誤啊,服務(wù)器自定義錯誤啊的種種情況.

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) {
       //獲取數(shù)據(jù)失敗
       if (value == null) {
           setValue(BaseErrorHandling.NULLERROR, "data can't be null");
       } else {
           super.postValue(value);
       }
   }

   @Override
   public void setValue(T value) {
       //獲取數(shù)據(jù)失敗
       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);
   }


}

用法如下
這是一個常規(guī)的P層獲取列表的操作

  @Override
    public void onCreat() {
        view.showLoading();
        userViewModel.getData().
                setCommonLiveDataCall(new CommonLiveData.CommonLiveDataCall() {
                    @Override
                    public void dataError(int errorCode, String msg) {
                      //說明獲取數(shù)據(jù)失敗了,我們切不管具體錯誤碼,通知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做了一個包裝,官方建議數(shù)據(jù)的改變我們交給Repertory(倉庫)來做,來達(dá)到方便擴(kuò)展的目的,我也這么建議,而且很有必要
如下是我們的UserViewModel,在構(gòu)造ViewModel的同時我們將IUserRepertory 倉庫的實(shí)現(xiàn)類傳入

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);
    }
}

下面模擬一個網(wǎng)絡(luò)數(shù)據(jù)的獲取

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,"網(wǎng)絡(luò)錯誤");
                liveData.setValue(users);
            }
        }, 1000);
    }
}

之后如果你要換數(shù)據(jù)獲取的方式,只需要更改dagger2配置文件,如下
我們就將這個數(shù)據(jù)的獲取方式換成了數(shù)據(jù)庫獲取,數(shù)據(jù)庫的獲取方式自己具體實(shí)現(xiàn),P層,view層,甚至viewmodel不需要做任何的變更

@Module
public class ViewModelModel {

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


}

好了,大體的一個實(shí)錄就是這樣的,我們還可以做更多的封裝,下面是幾點(diǎn)建議
1,每個數(shù)據(jù)結(jié)構(gòu)對應(yīng)一個viewModel,每個viewModel只對應(yīng)一個數(shù)據(jù)結(jié)構(gòu).
2,每個Repertory可以有多個數(shù)據(jù)結(jié)構(gòu)的改變,具體自己考慮,比方user相關(guān)的數(shù)據(jù)獲取可以放到一個倉庫,goods相關(guān)的可以放到一個倉庫等
3,關(guān)于dagger2,我建議可以將所有的viewmode寫到一個model配置文件中,改的時候也好找,其他的不涉及到第三方的構(gòu)造或者接口的直接構(gòu)造加個injiect就可以了,沒必要單獨(dú)出來寫個model,所以總之model我建議分類放(viewmodel,adapter等等)
4,Component 注入器更具自己考慮寫,我建議更具ui界面分開寫,方便管理,畢竟需要注入的東西課能都不一樣,當(dāng)然寫到一個里面也可以,便于封裝.

差不多就這些內(nèi)容,有什么不足的請多多指教.

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容