androidMVP的尝试

在android项目开发中,随着功能不断迭代,代码量通常也会随之不断增加,维护成本越来越高。

作为开发者,笔者经常会被杂乱的逻辑搞的焦头烂额,不禁思考:什么样的结构能够简化开发,同时又能降低维护成本?

当下开发中比较推崇的是三层架构,典型代表即是MVP。笔者在此将最近对MVP的理解与心得与大家分享一下。

一直以来,笔者对android中MVP模式的理解并没有很透彻,最近看了kymjs的文章《用MVP架构开发Android应用》,有一种醍醐灌顶的感觉,特在此感谢作者。作者的MVP框架也放到了Github上——TheMVP,感兴趣的朋友可以多加关注。

就个人的理解,目前对MVP的分歧,主要在于Activity的归属问题上。大部分人认为Activity属于V层,但也有部分人认为Activity属于P层。

Activity属于V层?

以登陆页面为例,两个Edittext分别获取账号和密码,一个Button用于登陆。Activity首先setContentView,随后添加Button的点击监听,onClick中获取账号和密码进行请求,一气呵成。流程中setContentView,对Button进行监听,均需要对ui元素直接操作。若是复杂页面,则ui元素的直接操作将会更多,Activity作为V层有天然的优势。

Activity作为P层?

Activity中提供了非常多的回调方法,同时作为Context的实现类,各种Manager均可通过Activity来调用。可以说,我们能想到的绝大部分逻辑操作均可以在Activity来完成,Activity也有作为P层的潜质。

既然Activity作为V层、P层均有一定道理,我们又应该如何理解Activity呢?

Activity无所不能

Activity是上帝类,我们在Activity中做任何事情都是可以的。

试想一下,在一个动态布局的页面中,我们可能不需要xml来写布局,所有控件都是代码生成。各种事件也均有Activity来处理,其中也包括了file操作等。一个Activity将MVP的操作都做了,是不是很万能?

  

我们是否需要使用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层提供接口来处理,但会麻烦一些。

笔者水平有限,很多问题都没有解决,也仅是提供一种思路,望园友能够不吝赐教,共同进步~

原文地址:https://www.cnblogs.com/puff/p/5097555.html