[关闭]
@shark0017 2015-12-30T08:20:17.000000Z 字数 4130 阅读 2029

Android模式

模式


模式

QQ截圖20151217110104.png-7.2kB
我们的业务里面经常会出现这么三种东西,这三个东西一定要广义理解为层,他们绝对不是狭义的类对象(因为有些语言中会有view、controller、model这样的类)。那么所谓的各种模式就是这三者的不同的组合和通信方式。

要说明白这个问题,就要知道哪些是v,哪些是m,哪些是c。

V:视图层。android中的view,比如textview,button这样的,自定义的view当然也属于此类。view层是可以独立数据而显示的,它里面没有什么程序逻辑,仅仅是做表现。
除了这些类对象外,activity、fragment算不算view呢?adapter是什么呢?如果看前面的定义,他们貌似都对对不上号,不过我们可以进行思维方式的转换,人为定义它们的意义。

M:用于封装业务逻辑相关的数据以及对数据的处理方法。M本身是完全独立的个体,并且应该能被监听到。M不应该知道view的存在,方便进行复用。

C:控制层。用来控制数据、处理view和数据的交互,它处理来自view的交互信号和数据层的改变。早期的c层是键盘和鼠标,所以早期是可以直接面向用户进行操作的,但是在移动时代慢慢变成了一个纯的控制对象。

MVC

QQ截圖20151217111049.png-40.6kB

我刚接触android的时候就听过android是MVC模式的,因为android的view层可以理解为xml层,自定义view也是较为独立的。但之后发现很多项目中在自定义view中处理了很多业务逻辑,而且在activity中做了view和controller的事情,慢慢android项目就成了下面这样:
举例:

  1. /**
  2. * controller 层
  3. */
  4. public class MainActivity extends AppCompatActivity {
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main); // view层
  9. final Button button = (Button) findViewById(R.id.button); // 绑定view
  10. button.setOnClickListener(new View.OnClickListener() {
  11. @Override
  12. public void onClick(View v) {
  13. // request network 做数据处理
  14. HttpUtil.doPostAsync("http://www.baidu.com", "kale", new HttpUtil.CallBack() {
  15. @Override
  16. public void onRequestComplete(String result) {
  17. button.setText(result); // 更新视图, From network
  18. }
  19. });
  20. }
  21. });
  22. }
  23. }

为了说明的简便,这个代码没进行网络分层(activity中写了url,无网络层)。

分析
在这段代码中我们是认为xml文件就是view层,activity做view和model的绑定和关联操作。但在这种情况下activity就会越来越臃肿,即使有fragment的加入,但还是没办法做到给activity减负的工作。
我先抛几个问题:
activity需要做view的绑定工作么?
activity需要做view的动画操作么?
activity得到网络的结果后需要做处理结果的操作么?
activity需要做不同状态下view的状态改变的操作么?

如果你觉得activity仅仅是c,那么它做了绑定视图和相应视图事件的工作,甚至还会有视图动画的操作,它就不仅仅是c了。如果你觉得它是v,它又做了很多事物操作,不仅仅是纯粹的v。为了解决activity臃肿和含义不清的矛盾,慢慢出现了mvp规范。

MVP

QQ截圖20151217163722.png-37.3kB

mvp是为了解决activity的尴尬处境而出现的,其实仅仅是把mvc做了一个逻辑比较清晰的改造,产生了清晰的封层。这是某个使用mvp的项目中activity的代码:

  1. presenter = new AppInfoPresenter(); // p层
  2. mShowPackageNameBtn.setOnClickListener(v -> {
  3. v.setEnabled(false); // activity变成v层,这里控制view的相关状态
  4. // 点击后的事情交给p做,p做完后应该给v一个回调。为了说明简单,这里是同步回调。
  5. string name = presenter.getPackageInfo(getApplication());
  6. mShowPackageNameBtn.setText(name); // 得到回调后更新视图
  7. });

现在的这种方式将activity和xml文件变成了一个v,那么所有事物都是交由p做。这样的好处就是model对外层不知情,p对view不知情。于是变成了这样的一个蛋形结构:
QQ截圖20151230160300.png-15.3kB

内层对外层不知情的好处就是内层可以随意地做复用,坏处就是需要建立相互通信地机制,常见的就是各种回调。当然,你可以用Rx的方式很简单的做回调,但是我们是否真的要采用这种严格的回调方式?

比如让P对v知晓,v也知道p的存在,可以么?来看看这种方案是什么样的。
首先让activity实现某个接口比如IAppInfoP,然后让P调用这个接口对象进行交互。

Activity中的代码:

  1. presenter = new AppInfoPresenter(); // p层
  2. mShowPackageNameBtn.setOnClickListener(v -> {
  3. v.setEnabled(false); // activity变成v层,这里控制view的相关状态
  4. // 点击后的事情交给p做,不会给view回调
  5. presenter.getPackageInfo(getApplication());
  6. });

Presenter中的代码:

  1. public class AppInfoPresenter extends BasePresenter<IAppInfoUi> implements IAppInfoP {
  2. @Override
  3. public void getPackageInfo(Context context) {
  4. // p对v知情,直接调用v中的public方法。getView其实得到的就是activity的接口对象
  5. getView().onGotPackageInfo(context.getPackageName());
  6. }

这个的好处很明显,activity变了纯的view,而p和v的交互也不用各种回调了(将activity整体变成了一个回调接口)。那,这种方式有不会有什么问题?
p知道了v,那么p的复用性就丧失了。所以你看到了我利用接口来降低了互相知情的而造成的影响。但这样,你就必须在写view的时候定义很多接口。如果不定义接口,你就必须把activity用fragment做分割。因为业务需求是经常改变的,给p和v定义接口,每次改都好麻烦,不定义接口可以么?

好吧,看下不定义接口的情况下有没有什么问题:
Presenter:

  1. public class Presenter {
  2. public MainActivity mActivity;
  3. // 省略初始化MainActivity的代码
  4. public void loadData() {
  5. // request network 做数据处理
  6. HttpUtil.doPostAsync("http://www.baidu.com", "kale", (result)-> {
  7. mActivity.fromNetwork(result); // 更新视图, From network
  8. }
  9. });
  10. }
  11. }

Activity:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. final Presenter presenter = new Presenter(this); // presenter
  4. button.setOnClickListener(v-> {
  5. presenter.loadData(); // 让p不知道这是因为点击而触发的动作
  6. }
  7. });
  8. }
  9. /**
  10. * 被presenter调用
  11. */
  12. public void fromNetwork(String name) {
  13. button.setText(name); // 更新视图, From network
  14. }
  15. }

用这种方式p中包含了v对象,那么不用写任何回调就能直接触发v的动作,而且不用写接口和回调,甚至还可以支持一个view有多个p的需求。但是如果你这个view被复用了(activity基本不会被复用),那p和v就必须一起变。这种方案的一大坏处就是灵活性会比较低,设计上就定死了v可以有多个p,但一个p只能对应一个v。

MVVM

我们看到了上述mvp的两种实现方案,第一种灵活但是写起来复杂,第二种简单,但是灵活性不足。mvvm利用数据绑定的东西,自动化实现了第一种的回调模式,而且也不用写任何接口,同时也因为在p中操作的就是单纯的数据对象,所以不会出现p和v的关联。
QQ截圖20151217164332.png-22.5kB
上面图说明的是首先我们看到在mvvm中m、v、vm是完全独立的,viewdata是一个中间产物,是一个view的数据对象,它可以注入到任何view中。从右边来看我们操作的就是一个逻辑结构,完全不用管理view相关的东西,在view层只需要把viewdata注入到view层中就行了,这样不同的view层可以对应一个或多个viewdata,viewdata也可以对应多个view。现在蛋形结构变成了这样:
QQ截圖20151217165531.png-23.6kB

它的好处就不用说了,不用写回调,而且还可以轻松的测试。至于怎么实现,以后会说到。

参考自:
http://www.jianshu.com/p/add73330d106
http://www.jianshu.com/p/e7b6ff1bc360
http://www.jianshu.com/p/918719151e72
http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注