Bye bye MVP Welcome T-MVVM
前言
自從官方mvp的Sample出來後,鬧得熱火朝天的mvp,小碼也未能倖免加入MVP大坑中,入坑近2年的MVP的架構終於要說拜拜了,最近由於公司專案相對穩定,做了一次較大的重構,原來的MVP架構切換到了T-MVVM。整個專案清爽了許多。
問題
MVP缺點: * Presenter中除了邏輯以外,還有大量的View->Model,Model->View的邏輯操作,造成Presenter臃腫,維護困難。 * 對UI的渲染放在了Presenter中,所以UI和Presenter的互動會過於頻繁。 * Presenter過多地渲染了UI,往往會使得它與特定的UI的互動頻繁。一旦UI變動,Presenter也需要變 * 介面暴增,可以說程式碼量成倍增長,互動都需要通過介面傳遞資訊,讓人無法忍受.
基本上用過mvp的碼友們都能發現了以上諸多弊端。於是小碼就嘗試從傳統的MVP過度到T-MVVM,深度結構,告別繁瑣的接傳遞資訊。
如果專案業務不是很多或者業務相對簡單,其實完全沒有必要使用mvp,反而讓專案變得更為複雜。
T-MVVM程式碼,如果有幫助記得star哦 https://github.com/SelfZhangTQ/T-MVVM
基於ViewModel,LiveData,Retrofit,Rxjava實現T-MVVM體系結構的架構,泛型限定,深度解耦。
ViewModel優點: * 同步關聯生命週期, * 資料共享 , * 複用性強 , LiveData優點: * 確保UI介面的資料狀態, * 沒有記憶體洩漏,不會因為Activity的不可見導致Crash, * 不用再人為的處理生命週期, * 共享資源,
此架構未使用DataBinding原由:
* 資料繫結增加Bug除錯難度, * 複雜的頁面,model也會很大,雖然使用方便了也很容易保證了資料的一致性,當時長期持有,不利於釋放記憶體, * 資料雙向繫結不利於View重用,
T-MVVM架構分層程式碼
1:先定義BaseViewModel基類
/** * @author:tqzhang on 18/7/26 16:15 */ public class BaseViewModel<T extends BaseRepository> extends AndroidViewModel { public T mRepository; public BaseViewModel(@NonNull Application application) { super(application); mRepository = TUtil.getNewInstance(this, 0); } @Override protected void onCleared() { super.onCleared(); if (mRepository != null) { mRepository.unSubscribe(); } } }
BaseViewModel通過泛型型別引數BaseRepository子類初始化Repository資料倉庫,同時在activity/fragment走onDestroy()生命週期方法時 BaseViewModel回撥onCleared,即頁面銷燬是用來取消網路請求或資源釋放等操作。
正常開發一般不建議直接通過ViewModel獲取網路資料,這裡我們將工作交給一個新的模組Repository。Repository只負責資料處理,提供乾淨的api,方便切換資料來源。
2:再定義BaseRepository
public abstract class BaseRepository { protected ApiService apiService; public BaseRepository() { if (null == apiService) { apiService = HttpHelper.getInstance().create(ApiService.class); } } private CompositeSubscription mCompositeSubscription; protected void addSubscribe(Subscription subscription) { if (mCompositeSubscription == null) { mCompositeSubscription = new CompositeSubscription(); } mCompositeSubscription.add(subscription); } public void unSubscribe() { if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) { mCompositeSubscription.clear(); } }
BaseRepository中內容相對簡單,主要是獲取ApiService和網路請求訂閱容器,方便管理網路請求。
3:然後自定義AbsLifecycleFragment基類繼承BaseFragment,BaseFragment可自行編寫。如不需要使用T-MVVM,可自行繼承BaseFragment,互不影響。
public abstract class AbsLifecycleFragment<T extends BaseViewModel> extends BaseFragment{ protected T mViewModel; /** * init view * @param state */ @Override public void initView(Bundle state) { mViewModel = VMProviders(this, TUtil.getInstance(this, 0)); if (null != mViewModel) { dataObserver(); } } /** * create ViewModelProviders * * @return ViewModel */ protected <T extends ViewModel> T VMProviders(BaseFragment fragment, @NonNull Class<T> modelClass) { return ViewModelProviders.of(fragment).get(modelClass); } protected void dataObserver() { } /** * 獲取網路資料 */ protected void getRemoteData() { } }
在initView方法中通過BaseViewModel子類泛型型別引數獲取Class<T>,在通過ViewModelProviders.of(fragment).get(modelClass))例項化ViewModel,
到此我們的基類基本編寫完畢。
4:下面我們以一個簡單業務實戰下,獲取文章列表。
4-1:ArticleFragment
/** * @author:zhangtianqiu on 18/7/2 14:40 */ public class ArticleFragment extends AbsLifecycleFragment<ArticleViewModel> { protected TRecyclerView mRecyclerView; protected StaggeredGridLayoutManager layoutManager; protected MultiTypeAdapter adapter; public static ArticleFragment newInstance() { return new ArticleFragment(); } @Override public int getLayoutResId() { return R.layout.fragment_list; } @Override public void initView(Bundle state) { super.initView(state); layoutManager=new new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL); //獲取網路資料 getRemoteData(); } //初始化adapter public void initAdapter(){ adapter= new MultiTypeAdapter.Builder<>() .bindArray(ArticleInfoVo.class, new ArticleRem1ItemHolder(context) , new ArticleRem2ItemHolder(context) , new ArticleRem3ItemHolder(context)) .bind(HeaderVo.class, new HeaderViewHolder(context, rogressStyle.Pacman)) .bind(FootVo.class, new FootViewHolder(context, ProgressStyle.Pacman)) .build(); //資料觀察 @Override protected void dataObserver() { mViewModel.getArticleList().observe(this, articleVo -> { if (null != articleVo) { mRecyclerView.refreshComplete(articleVo.data.list, false); } }); } //獲取網路資料 @Override protected void getRemoteData() { mViewModel.getArticleList(typeId, lastId); } }
我們可以看出來ArticleFragment中只有UI初始化,發請網路請求action以及資料觀察更新UI,列表展示用了 TRecyclerView,歡迎star哦 面向holder開發高複用,多型別的重新整理庫,從此只關心你的列表的Item展示。
通過泛型除去了MVP中通過介面傳遞資訊的大量程式碼,
從此see you Mass implementation of interfaces。
4-1:ArticleViewModel
/** * @author:tqzhang on 18/7/26 16:15 */ public class ArticleViewModel extends BaseViewModel<ArticleRepository> { private MutableLiveData<ArticleVo> mArticleData; public ArticleViewModel(@NonNull Application application) { super(application); } public LiveData<ArticleVo> getArticleList() { if (mArticleData == null) { mArticleData = new MutableLiveData<>(); } return mArticleData; } public void getArticleList(String lectureLevel1, String lastId) { mRepository.loadArticleRemList(new CallBack<ArticleVo>() { @Override public void onNext(ArticleVo articleObject) { mArticleData.postValue(articleObject); } @Override public void onError(String e) { }} }); }
ArticleViewModel中持有資料觀察容器LiveData和真正發起網路請求動作,在接收到服務端返回的資料通過
mArticleData.postValue(articleObject);通知Observer資料的更改,此處需注意的是,setValue方法只能在主執行緒中呼叫,postValue可以在任何執行緒中呼叫,如果是在後臺子執行緒中更新LiveData的值,必須呼叫postValue。
4-3:ArticleRepository
/** * @author:tqzhang on 18/7/28 13:00 */ public class ArticleRepository extends BaseRepository { public void loadArticleRemList(final CallBack<ArticleVo> listener) { addSubscribe(apiService.getArticleRemList() .compose(RxSchedulers.io_main()) .subscribe(new RxSubscriber<ArticleVo>() { @Override public void onSuccess(ArticleVo articleObject) { listener.onNext(articleObject); } @Override public void onFailure(String msg) { listener.onError(msg); } })); }
最後我們的ArticleRepository中就提供資料,此處只提供了網路層的資料,在實際應用中可拆分類loacl data和remote data,可根據專案需求自行處理。
至此咋們一個簡單業務程式碼就完成了,是驢子是馬,拉出來溜溜就知道,實踐出真知,效果圖奉上:

1.gif
專案地址github地址: https://github.com/SelfZhangTQ/T-MVVM ,歡迎大家交流,star。