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层提供接口来处理,但会麻烦一些。

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

  • 相关阅读:
    linux定时器的使用
    6410 spi总线
    6410 spi 设备驱动
    qtcreator 安装后不能显示菜单
    基于Tiny 6410的内核移植 (NAND FLASH 、UBIFS篇)
    有关ajax跨域问题
    sqlmap的安装
    C# 连接数据库的配置方法
    初识Markdown
    ASP.NET 使用Ueditor富文本编辑器
  • 原文地址:https://www.cnblogs.com/puff/p/5097555.html
Copyright © 2011-2022 走看看