給大家推薦個開源專案 : T-MVVM
MVP缺點
-
Presenter中除了邏輯以外,還有大量的View->Model,Model->View的邏輯操作,造成Presenter臃腫,維護困難。
-
對UI的渲染放在了Presenter中,所以UI和Presenter的互動會過於頻繁。
-
Presenter過多地渲染了UI,往往會使得它與特定的UI的互動頻繁,一旦UI變動,Presenter也需要變。
-
介面暴增,可以說程式碼量成倍增長,互動都需要通過介面傳遞資訊,讓人無法忍受。
基本上用過MVP的碼友們都能發現了以上諸多弊端,於是小碼就嘗試從傳統的MVP過度到T-MVVM,深度結構,告別繁瑣的接傳遞資訊。
優秀的架構能讓軟體高內聚、低耦合、可維護、可擴充套件,其實,對於MVP或者MVVM沒有絕對好壞,MVP->MVVM只不過讓模型和檢視分離得更加的徹底,優化成了前者的缺點,如果專案業務不是很多或者業務相對簡單,其實完全沒有必要使用MVP/MVVM,反而讓專案變得更為複雜。
什麼是T-MVVM
基於ViewModel、LiveData、Retrofit、OkHttp和Rxjava實現的T-MVVM體系結構的架構,泛型限定,深度解耦。
ViewModel優點: * 同步關聯生命週期 * 資料共享 * 複用性強LiveData優點: * 確保UI介面的資料狀態 * 沒有記憶體洩漏,不會因為Activity的不可見導致Crash * 不用再人為的處理生命週期 * 共享資源
此架構未使用DataBinding原因:
* 資料繫結增加Bug除錯難度* 複雜的頁面,model也會很大,雖然使用方便了也很容易保證了資料的一致性,當長期持有,不利於釋放記憶體* 資料雙向繫結不利於View重用
T-MVVM的實現細節:
MVVM的呼叫和MVP類似,在MVP中全部由Presenter負責ViewModel之間的資料同步,而MVVM中ViewModel充當了Presenter的角色,ViewModel是View與Model的聯結器,持有可被觀察的資料持有者和網路請求操作,資料變更實時渲染UI。
如何使用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);}@Overrideprotected 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*/@Overridepublic 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 style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;">,在通過ViewModelProviders.of(fragment).get(modelClass))例項化ViewModel,到此我們的基類基本編寫完畢。</t>
4.下面我們以一個簡單業務實戰下,獲取文章列表。
4-1:ArticleFragment
/*** @author:tqzhang 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);mRecyclerView=findViewById(R.id.recycler_view);initAdapter();initRecyclerView();//獲取網路資料getRemoteData(); } public void initRecyclerView(){layoutManager=new new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);mRecyclerView.setAdapter(adapter);mRecyclerView.setLayoutManager(layoutManager);} //初始化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面向holder開發高複用,多型別的重新整理庫,從此只關心你的列表的Item展示。還通過泛型除去了MVP中通過介面傳遞資訊的大量程式碼,從此see you Mass implementation of interfaces。
4-2: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>() {@Overridepublic void onNext(ArticleVo articleObject) {mArticleData.postValue(articleObject);}@Overridepublic 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>() {@Overridepublic void onSuccess(ArticleVo articleObject) {listener.onNext(articleObject);}@Overridepublic void onFailure(String msg) {listener.onError(msg);}}));}
最後我們的ArticleRepository中就提供不含任何雜質的純淨的資料,此處只提供了網路層的資料,在實際應用中可拆分類loacl data和remote data,可根據實際專案需求自行處理。
至此一個簡單的業務程式碼就完成了,是驢子是馬,拉出來溜溜就知道,實踐出真知,效果圖奉上:

之前有碼友們提到多個網路請求如何處理,如果同一頁面有多個網路請求操作,其實多網路請求可以合併處理,統一返回結果然後組裝資料返回,同時也有利於控制空頁面的顯示邏輯。
市面上各種各樣開發架構,蘿蔔青菜各有所愛,沒有最好的架構,只有最適合自己的,大家在選型開發架構時理應多多斟酌,當前很火的架構並不一定適合自己,結合自身專案進行舍取。