前言:我们平时进行Android应用开发,是不是遇到以下问题? Activity或Fragment业务逻辑代码臃肿不堪,在复杂的业务逻辑下可阅读性差,就像拧在一起的麻绳,耦合严重。Activity或Fragment不仅要处理业务逻辑(事件、UI逻辑)还得与数据模型(Http网络请求、数据库)进行交互,所以让Activity或Fragment变得非常臃肿。MVP是MVC的升级版,使用MVP开发模式可以解决这个问题,让你的工程变的异常的清晰,代码可阅读性变的非常好。当然MVP并不是没有缺点的,后面再讲解。
Android应用本身就是一个MVC架构的工程。MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。
MVC一种软件设计模式,用一种业务逻辑(Controller)、数据(Model )、界面(View )显示分离的方法,将业务逻辑聚集到一个类里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。其中M层处理封装数据;V层处理界面的显示结果;C层用于业务逻辑控制的作用,来控制V层和M层通信以此来达到分离视图显示和业务逻辑层。
结合android应用,当用户出发事件的时候,view层会发送指令到controller层,接着controller去通知model层更新数据,model层更新完数据以后直接显示在view层上,这就是MVC的工作原理。
控制层Controller:控制器就是Activity 或Fragment。负责UI业务逻辑、事件处理、并且与数据模型交互。 Model层:数据库、Http请求、等数据获取方式获得的bean数据模型。 UI视图层 View: layout文件夹下的XML布局文件。
Android工程有java的class文件,有res文件夹,里面是各种资源,还有类似manifest文件等等。对于原生的Android项目来说,layout.xml里面的xml文件就对应于MVC的view层,里面都是一些view的布局代码,而各种java bean,还有一些类似repository类就对应于model层,至于controller层嘛,当然就是各种activity咯。大家可以试着套用我上面说的MVC的工作原理是理解。比如你的界面有一个按钮,按下这个按钮去网络上下载一个文件,这个按钮是view层的,是使用xml来写的,而那些和网络连接相关的代码写在其他类里,比如你可以写一个专门的networkHelper类,这个就是model层,那怎么连接这两层呢?是通过button.setOnClickListener()这个函数,这个函数就写在了activity中,对应于controller层。是不是很清晰。 大家想过这样会有什么问题吗?显然是有的,不然为什么会有MVP诞生呢,是吧。问题就在于xml作为view层,控制能力实在太弱了,你想去动态的改变一个页面的背景,或者动态的隐藏/显示一个按钮,这些都没办法在xml中做,只能把代码写在activity中,造成了activity既是controller层,又是view层的这样一个窘境。大家回想一下自己写的代码,如果是一个逻辑很复杂的页面,activity或者fragment是不是动辄上千行呢?这样不仅写起来麻烦,维护起来更是噩梦。 MVC还有一个重要的缺陷,大家看上面那幅图,view层和model层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。 正因为MVC有这样那样的缺点,所以才演化出了MVP种框架。
MVP作为MVC的演化,解决了MVC不少的缺点,对于Android来说,MVP的model层相对于MVC是一样的,而activity和fragment不再是controller层,而是纯粹的view层,所有关于用户事件的转发全部交由presenter层处理。下面还是让我们看图
从图中就可以看出,最明显的差别就是view层和model层不再相互可知,完全的解耦,取而代之的presenter层充当了桥梁的作用,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。看到这里大家可能会问,虽然view层和model层解耦了,但是view层和presenter层不是耦合在一起了吗?其实不是的,对于view层和presenter层的通信,我们是可以通过接口实现的,具体的意思就是说我们的activity,fragment可以去实现实现定义好的接口,而在对应的presenter中通过接口调用方法。不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试。这就解决了MVC模式中测试,维护难的问题。
光是讲一堆的理论很难让理解,下面来个常见的小例子要演示一下! 一个在移动应用非常常见的列表!
再看一下目录工厂的包结构
布局文件我就不介绍了,大家都能猜到了! 首先先看一下MainConstract ,MainConstract 里面包含了View、Model、Presenter三层的接口用于给对应的三层实现。把它写在一个类里是为了减少class文件的创建。
public class MainConstract { public interface IMainView { //刷新列表数据 void refreshData(List<String> data); //刷新数据失败提示 void showRefreshError(); } public interface IMainModel { //访问网络获取数据模型 void getListData(OnHttpCallBack<List<String>> onHttpCallBack); } public interface IMainPresenter { //获得Model层返回的列表数据 void getListData(); } }大家可能注意到上面的OnHttpCallBack,它是为了给请求数据成功和失败进行回调的接口
public interface OnHttpCallBack<T> { void onSuccess(T data); void onFail(String error); }Model层的代码
public class MainModel implements MainConstract.IMainModel { @Override public void getListData(final OnHttpCallBack<List<String>> onHttpCallBack) { new AsyncTask<Void,Void,List<String>>(){ @Override protected List<String> doInBackground(Void... voids) { //模拟请求网络获得数据 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } List<String> data=new ArrayList<>(); for (int i = 0; i < 10; i++) { data.add("条目:"+i); } return data; } @Override protected void onPostExecute(List<String> data) { super.onPostExecute(data); //成功获取数据回调 onHttpCallBack.onSuccess(data); } @Override protected void onCancelled(List<String> data) { super.onCancelled(data); //获取数据失败回调 onHttpCallBack.onFail("获取数据超时...."); } }.execute(); } }可以看到model层只负责数据的获取和解析成对应的数model,这个和MVC的Model层一致,成为一个独立的单元,其他层不需要关心它是如何实现,达到解耦的目的。
View层代码
public class MainActivity extends AppCompatActivity implements MainConstract.IMainView { ListView listView; SwipeRefreshLayout swipeRefreshLayout; private List<String> mData=new ArrayList<>(); private MainPresenter mainPresenter; private ListAdapter listAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.listview); //初始化Presenter层 mainPresenter = new MainPresenter(this); listAdapter = new ListAdapter(this,mData); listView.setAdapter(listAdapter); //初始化填充数据 mainPresenter.getListData(); } @Override public void refreshData(List<String> data) { listAdapter.updateData(data); } @Override public void showRefreshError() { Toast.makeText(this,"数据获取失败!",Toast.LENGTH_LONG).show(); } }可以看到Activity彻底的成为View层,只负责UI的显示、隐藏、数据的填充,实际上到底在什么时候显示隐藏填充数据,View层完全不需要考虑,只需要提供接口给Presenter层调用,把所有的业务逻辑交给Presenter层处理。
Presenter层代码
public class MainPresenter implements MainConstract.IMainPresenter { private MainModel mainModel; private MainConstract.IMainView mainView; public MainPresenter(MainConstract.IMainView mainView){ this.mainView=mainView; mainModel = new MainModel(); } @Override public void getListData() { mainModel.getListData(new OnHttpCallBack<List<String>>() { @Override public void onSuccess(List<String> data) { //数据获取成功后刷新adapter mainView.refreshData(data); } @Override public void onFail(String error) { //数据获取失败,提示语 mainView.showRefreshError(); } }); } }Presenter层负责业务的逻辑处理并且和Model层进行交互,获得数据和如何展示数据! 理解完上面的代码后结构是不是变清晰了?但是class文件和代码量的增大这个问题确实无可避免。但是换来的好处在中大型多人开发的项目上会得到非常好的表现。
一个小工程个人觉得没必要使用这种模式来开发了,因为并不会起到让代码结构变清晰的目的,反而会因为它创建过多的类文件造成过多的开发成本。 使用MVP最能够体现出它的优势的在中型以上的项目,最好是多人开发上,开发人员越多,越能体现它的优势!
代码量增大,并且造成类文件和接口的成倍的增加,降低了编码速度,但是换来的是整个项目的结构变的非常清晰、解耦。
总结 讲了那么多,也就是为了让代码的阅读性提高,当然只靠MVP这种模式来编写代码肯定还是不够的,还有其他的一些提高代码可阅读性的方式。下一篇就来分享一下我在开发中的一些提高代码可阅读性的一些经验总结。
源码下载