androidMVP的嘗試

在android項目開發中,隨着功能不斷迭代,代碼量一般也會隨之不斷增長,維護成本愈來愈高。android

做爲開發者,筆者常常會被雜亂的邏輯搞的焦頭爛額,不由思考:什麼樣的結構可以簡化開發,同時又能下降維護成本?git

當下開發中比較推崇的是三層架構,典型表明便是MVP。筆者在此將最近對MVP的理解與心得與你們分享一下。github

 

一直以來,筆者對android中MVP模式的理解並無很透徹,最近看了kymjs的文章《用MVP架構開發Android應用》,有一種醍醐灌頂的感受,特在此感謝做者。做者的MVP框架也放到了Github上——TheMVP,感興趣的朋友能夠多加關注。架構

 

就我的的理解,目前對MVP的分歧,主要在於Activity的歸屬問題上。大部分人認爲Activity屬於V層,但也有部分人認爲Activity屬於P層。app

Activity屬於V層?

以登錄頁面爲例,兩個Edittext分別獲取帳號和密碼,一個Button用於登錄。Activity首先setContentView,隨後添加Button的點擊監聽,onClick中獲取帳號和密碼進行請求,一鼓作氣。流程中setContentView,對Button進行監聽,均須要對ui元素直接操做。如果複雜頁面,則ui元素的直接操做將會更多,Activity做爲V層有自然的優點。框架

Activity做爲P層?

Activity中提供了很是多的回調方法,同時做爲Context的實現類,各類Manager都可經過Activity來調用。能夠說,咱們能想到的絕大部分邏輯操做都可以在Activity來完成,Activity也有做爲P層的潛質。ide

 

既然Activity做爲V層、P層均有必定道理,咱們又應該如何理解Activity呢?佈局

Activity無所不能

Activity是上帝類,咱們在Activity中作任何事情都是能夠的。ui

試想一下,在一個動態佈局的頁面中,咱們可能不須要xml來寫佈局,全部控件都是代碼生成。各類事件也均有Activity來處理,其中也包括了file操做等。一個Activity將MVP的操做都作了,是否是很萬能?this

  

咱們是否須要使用MV*模式?

僅從筆者的開發經驗來看,對MV*模式的使用並很少,大部分狀況下采用Activity/Fragment+Model層。

當咱們開發一個邏輯並不特別複雜的app時,只要抽象合理,代碼應當不難維護,也沒必要藉助MV*模式了。

但隨着PM的奇思妙想愈來愈多,項目越作越大,一個Activity可能幾千行,各類邏輯交織,顯得異常複雜。這時候咱們就能夠考慮經過MV*來拯救咱們了!

換句話說,採用MV*模式自己也是一種無奈。業務過於複雜,只能經過拆分層次,將複雜的問題分而治之。

Activity在V層和P層哪一種更合理?

Activity由於過於全面,放在哪層均可以。不過正由於它無可替代,無論做爲哪層,都須要取捨。

做爲V層,則應該將非UI操做放到自定義的P層去。自定義P層自己不具有不少功能(譬如onDestroy釋放handler等),這些都須要Activity提供接口實現。

做爲P層,則須要將UI操做放到自定義的V層去,UI相關的操做由V層處理,P層不作干預。

 

筆者認爲Activity做爲P層較爲方便,同時試着將Activity做爲P層實現了一個比較簡單且並不完善的MVP,僅供參考。

思路

M層沒有給出,P層是繼承於Activity的PresenterAdapter,V層是自定義的ViewAdapter。

P層的業務處理,不少都要反映在V層上,所以咱們的Activity持有ViewAdapter。

當頁面較爲複雜的時候,可能須要作不少UI處理,ViewAdapter提供各類接口給PresenterAdapter操做也是比較麻煩的。咱們可讓P、V持有同一個狀態集Info,P修改Info,V根據Info對UI進行渲染。

  • PresenterAdapter持有IView(主要爲ViewAdapter實現類)和BaseInfo的實現類,初始化二者,並將BaseInfo綁定到IView上。
 1 /**
 2  * Created by puff on 2016/1/1.
 3  */
 4 public abstract class PresenterAdapter<T extends IView, U extends BaseInfo> extends AppCompatActivity implements IPresenter<T, U> {
 5 
 6     private T mView;
 7     private U mInfo;
 8 
 9     @Override
10     protected void onCreate(Bundle savedInstanceState) {
11         super.onCreate(savedInstanceState);
12         //初始化view和info,並關聯
13         mView = initIView();
14         mInfo = initInfo();
15         mView.init(getLayoutInflater());
16         mView.setInfo(mInfo);
17         setContentView(mView.getRootView());
18     }
19 
20     @Override
21     public U getInfo() {
22         return mInfo;
23     }
24 
25     @Override
26     public T getView() {
27         return mView;
28     }

  • BaseInfo做爲狀態集,每對V/P均要實現一個。
 1 /**
 2  * Created by puff on 2016/1/1.
 3  */
 4 public class BaseInfo {
 5     private boolean shouldOpenDialog;
 6     private boolean shouldCloseDialog;
 7     private String dialogTitle;
 8     private String dialogMessage;
 9 
10     public boolean isShouldCloseDialog() {
11         return shouldCloseDialog;
12     }
13 
14     public void setShouldCloseDialog(boolean shouldCloseDialog) {
15         this.shouldCloseDialog = shouldCloseDialog;
16     }
17 
18     public boolean isShouldOpenDialog() {
19         return shouldOpenDialog;
20     }
21 
22     public void setShouldOpenDialog(boolean shouldOpenDialog) {
23         this.shouldOpenDialog = shouldOpenDialog;
24     }
25 
26     public String getDialogTitle() {
27         return dialogTitle;
28     }
29 
30     public void setDialogTitle(String dialogTitle) {
31         this.dialogTitle = dialogTitle;
32     }
33 
34     public String getDialogMessage() {
35         return dialogMessage;
36     }
37 
38     public void setDialogMessage(String dialogMessage) {
39         this.dialogMessage = dialogMessage;
40     }
41 }

  • ViewAdapter實現了IView接口,每次Activity調用refreshView時進行UI的從新渲染
 1 /**
 2  * Created by puff on 2016/1/1.
 3  */
 4 public abstract class ViewAdapter<T extends BaseInfo> implements IView<T> {
 5     /**
 6      * 根視圖,用來進行view操做
 7      */
 8     private View mRootView;
 9     /**
10      * 狀態集T
11      * View,Presenter經過T通訊,T更新進行refresh
12      */
13     private T mInfo;
14     /**
15      * 通用Dialog共用
16      */
17     private ProgressDialog mDialog;
18 
19     @Override
20     public void init(LayoutInflater inflater) {
21         mRootView = initViews(inflater);
22     }
23 
24     /**
25      * 實例化視圖
26      *
27      * @param inflater
28      * @return 根視圖
29      */
30     protected abstract View initViews(LayoutInflater inflater);
31 
32     @Override
33     public View getRootView() {
34         return mRootView;
35     }
36 
37     @Override
38     public void refreshView() {
39         if (mInfo == null) {
40             return;
41         }
42         if (mInfo.isShouldOpenDialog()) {
43             showDialog();
44             mInfo.setShouldOpenDialog(false);
45         }
46         if (mInfo.isShouldCloseDialog()) {
47             hideDialog();
48             mInfo.setShouldCloseDialog(false);
49         }
50     }
51 //其餘操做
52

 

筆者經過這個結構,完成了一個簡單的app,可以知足開發的基本需求。

PresenterAdapter只做業務處理,業務處理結果反映在狀態集上,基本上都是針對數據操做的。而ViewAdapter則專一於經過狀態集進行UI渲染。

特殊狀況

包含AdapterView的頁面複雜一些,由於大部分狀況下咱們須要實現BaseAdapter進行AdapterView的數據填充。

BaseAdapter其實和咱們提到的ViewAdapter有類似之處,均是View的管理者,經過數據進行View的渲染。因此咱們也能夠把BaseAdapter的實現類看成View層來處理。只不過狀態集中須要有一個List<XX>來供BaseAdapter使用了。

 

尚存問題

這個思路仍是有不少問題

Fragment要如何處理?

Fragment包含了不少類Activity的功能,生命週期也很是複雜,在複雜應用中常常出現一個Activity包含多個Fragment的場景(Activity->ViewPager->Fragment,這種方式就很複雜),Fragment如何處理,筆者尚未想到很好的辦法。

ActionBar,DecorView這種自然存在於Activity中的View如何處理?

ActionBar能夠經過其餘方式實現,DecorView沒有想到什麼好辦法。

setListener放在P層,通常都須要經過R.id,這樣就引用了View?

確實存在這個問題,也能夠經過V層提供接口來處理,但會麻煩一些。

 

筆者水平有限,不少問題都沒有解決,也僅是提供一種思路,望園友可以不吝賜教,共同進步~

相關文章
相關標籤/搜索