在android項目開發中,隨着功能不斷迭代,代碼量一般也會隨之不斷增長,維護成本愈來愈高。android
做爲開發者,筆者常常會被雜亂的邏輯搞的焦頭爛額,不由思考:什麼樣的結構可以簡化開發,同時又能下降維護成本?git
當下開發中比較推崇的是三層架構,典型表明便是MVP。筆者在此將最近對MVP的理解與心得與你們分享一下。github
一直以來,筆者對android中MVP模式的理解並無很透徹,最近看了kymjs的文章《用MVP架構開發Android應用》,有一種醍醐灌頂的感受,特在此感謝做者。做者的MVP框架也放到了Github上——TheMVP,感興趣的朋友能夠多加關注。架構
就我的的理解,目前對MVP的分歧,主要在於Activity的歸屬問題上。大部分人認爲Activity屬於V層,但也有部分人認爲Activity屬於P層。app
以登錄頁面爲例,兩個Edittext分別獲取帳號和密碼,一個Button用於登錄。Activity首先setContentView,隨後添加Button的點擊監聽,onClick中獲取帳號和密碼進行請求,一鼓作氣。流程中setContentView,對Button進行監聽,均須要對ui元素直接操做。如果複雜頁面,則ui元素的直接操做將會更多,Activity做爲V層有自然的優點。框架
Activity中提供了很是多的回調方法,同時做爲Context的實現類,各類Manager都可經過Activity來調用。能夠說,咱們能想到的絕大部分邏輯操做都可以在Activity來完成,Activity也有做爲P層的潛質。ide
既然Activity做爲V層、P層均有必定道理,咱們又應該如何理解Activity呢?佈局
Activity是上帝類,咱們在Activity中作任何事情都是能夠的。ui
試想一下,在一個動態佈局的頁面中,咱們可能不須要xml來寫佈局,全部控件都是代碼生成。各類事件也均有Activity來處理,其中也包括了file操做等。一個Activity將MVP的操做都作了,是否是很萬能?this
僅從筆者的開發經驗來看,對MV*模式的使用並很少,大部分狀況下采用Activity/Fragment+Model層。
當咱們開發一個邏輯並不特別複雜的app時,只要抽象合理,代碼應當不難維護,也沒必要藉助MV*模式了。
但隨着PM的奇思妙想愈來愈多,項目越作越大,一個Activity可能幾千行,各類邏輯交織,顯得異常複雜。這時候咱們就能夠考慮經過MV*來拯救咱們了!
換句話說,採用MV*模式自己也是一種無奈。業務過於複雜,只能經過拆分層次,將複雜的問題分而治之。
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進行渲染。
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 }
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 }
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層提供接口來處理,但會麻煩一些。
筆者水平有限,不少問題都沒有解決,也僅是提供一種思路,望園友可以不吝賜教,共同進步~