zoukankan      html  css  js  c++  java
  • 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层提供接口来处理,但会麻烦一些。

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

  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/puff/p/5097555.html
Copyright © 2011-2022 走看看