最近几年的移动端开发越来越火,功能越来越强大,处理业务越来越复杂,因此对系统扩展性的要求越来越高。而为了更好地进行移动端架构设计,我们最常用的就是MVC和MVP,今天本篇博客就和大家一起聊一聊这两种框架设计。
MVC框架
MVC的定义
MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式,而C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新,这与《设计模式》中的观察者模式是完全一样。
为何用MVC
从用户的角度出发,用户可以根据自己的需求,选择自己合适的浏览数据的方式。
从开发者的角度,MVC把应用程序的逻辑层与界面是完全分开的,这样,界面设计人员可以直接参与到界面开发,程序员就可以把精力放在逻辑层上。而不是像以前那样,设计人员把所有的材料交给开发人员,由开发人员来实现界面。
MVC的通信方式
首先,View发送命令到Controller,然后Controller处理完业务逻辑后让Model改变状态,最后由Model将新的数据发送到View,用户得到数据响应。
Android中的MVC
1.视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。
2.控制层(Controller):Android的控制层通常是在Acitvity中实现。
3.模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。
MVP框架
MVP的定义
MVP (Model-View-Presenter):MVP其实是由MVC演变而来的,其中的M依然是指逻辑模型,V依然是指视图模型,而P(中间桥梁)则代替了C成为了逻辑控制器的角色。
MVC和MVP到底有啥区别
区别就在于MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。我们知道在MVC里,View是可以直接访问Model的。从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,即View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。
MVP的通信方式
首先MVP各部分之间的通信都是双向的,但是唯独View与Model之间是不发生联系的,二者之间的通信都是通过Presenter传递的。在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。在这个过程中,View是很简单的,能够把信息显示清楚就可以了。在后面,根据需要再随便更改View,而对Presenter没有任何的影响了。 如果要实现的UI比较复杂,而且相关的显示逻辑还跟Model有关系,就可以在View和Presenter之间放置一个Adapter。由这个 Adapter来访问Model和View,避免两者之间的关联。而同时,因为Adapter实现了View的接口,从而可以保证与Presenter之间接口的不变。这样就可以保证View和Presenter之间接口的简洁,又不失去UI的灵活性。 在MVP模式里,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接访问Model–这就是与MVC很大的不同之处。
MVP的优缺点
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。
MVVM框架
MVVM的定义
MVVM(Model-View-ViewModel):MVVM和MVP的区别其实不大,只不过是把presenter层换成了ViewModel层,再有就是View层和ViewModel层是相互绑定的关系,当我们更新ViewModel层的数据的时候,View层会相应的更新UI。
MVVM的通信方式
MVVM它采用的是数据绑定(data-binding)方式,而且是双向绑定:View绑定到ViewModel,然后执行一些命令在向它请求一个动作。而反过来,ViewModel跟Model通讯,告诉它更新来响应UI。
MVVM优点
以下内容来自百度百科
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点
低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
MVP案例实践
下面我选取MVP框架作为主要案例,给大家讲解如何将一个普通项目改造成MVP框架模式的项目,帮助大家理解MVP框架模式的意义。
传统项目结构
该项目中我们定义了一个学生信息列表,用来显示图片和文字;项目源码如下:
MainActivity.java
/** * 功能:MainActivity 用列表形式实现 * 作者:猴子搬来的救兵 * 博客地址:http://blog.csdn.net/mynameishuangshuai * 日期:2016/10/01 */public class MainActivity extends AppCompatActivity implements IStudentView{ ListView myList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myList = (ListView) findViewById(R.id.my_list); myList.setAdapter(new StudentAdapter(MainActivity.this)); } }1234567891011121314151617
StudentAdapter.java
/** * 功能:StudentAdapter * 作者:猴子搬来的救兵 * 博客地址:http://blog.csdn.net/mynameishuangshuai * 日期:2016/10/01 */public class StudentAdapter extends BaseAdapter { private LayoutInflater myInflater; private List<Student> data; public StudentAdapter(Context context, List<Student> data) { myInflater = LayoutInflater.from(context); this.data = data; } @Override public int getCount() { return DataUtils.stuSize(); } @Override public Object getItem(int position) { return DataUtils.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View stuView = myInflater.inflate(R.layout.stu_item, null); Student student = DataUtils.get(position); ImageView imgStu = (ImageView) stuView.findViewById(R.id.img_stu); imgStu.setImageResource(student.getStuImg()); TextView tvStu = (TextView) stuView.findViewById(R.id.tv_name); tvStu.setText(student.getName()); return stuView; } }12345678910111213141516171819202122232425262728293031323334353637383940414243
DataUtils.java
/** * 功能:初始化数据工具类 * 作者:猴子搬来的救兵 * 博客地址:http://blog.csdn.net/mynameishuangshuai * 日期:2016/10/01 */public class DataUtils { public final static List<Student> stuData = new ArrayList<Student>(); public static Student get(int i){ return stuData.get(i); } public static int stuSize(){ return stuData.size(); } static { stuData.add(new Student("张三",R.mipmap.ic_launcher)); stuData.add(new Student("李四",R.mipmap.ic_launcher)); stuData.add(new Student("王五",R.mipmap.ic_launcher)); stuData.add(new Student("赵六",R.mipmap.ic_launcher)); stuData.add(new Student("陈七",R.mipmap.ic_launcher)); stuData.add(new Student("孙八",R.mipmap.ic_launcher)); stuData.add(new Student("猴子搬来的救兵",R.mipmap.ic_launcher)); } }1234567891011121314151617181920212223242526272829
Student.java
/** * 功能:学生Bean * 作者:猴子搬来的救兵 * 博客地址:http://blog.csdn.net/mynameishuangshuai * 日期:2016/10/01 */public class Student { private String name; private int stuImg; public Student() { } public Student(String name, int stuImg) { this.name = name; this.stuImg = stuImg; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getStuImg() { return stuImg; } public void setStuImg(int stuImg) { this.stuImg = stuImg; } }123456789101112131415161718192021222324252627282930313233343536
项目运行结果:
MVP改造结构
改造后的项目,我们增加了三大模块,分别是:
Model包负责处理数据
View包负责显示处理
Presenter包是中间桥梁,负责Model和View的交互
通过MVP框架实现数据适配
定义Model层接口
public interface IStudentModel { // 加载数据 void loadStudent(StudentOnLoadListener listener); interface StudentOnLoadListener{ void onComplete(List<Student> students); } }1234567
定义View层接口
public interface IStudentView { // 显示进度 void showLoading(); // 显示学生 void showStudents(List<Student> students); }123456
添加Model层
/** * 功能:StudentModel 第一次数据处理 * 作者:猴子搬来的救兵 * 博客地址:http://blog.csdn.net/mynameishuangshuai * 日期:2016/10/13 0013 */public class StudentModelImplOne implements IStudentModel { @Override public void loadStudent(StudentOnLoadListener listener) { Log.i("castiel","执行了StudentModelImplOne数据加载"); //模拟Json数据 List<Student> jsonStu1 = new ArrayList<Student>(); jsonStu1.add(new Student("张三11", R.mipmap.ic_launcher)); jsonStu1.add(new Student("李四11", R.mipmap.ic_launcher)); jsonStu1.add(new Student("王五11", R.mipmap.ic_launcher)); jsonStu1.add(new Student("赵六11", R.mipmap.ic_launcher)); jsonStu1.add(new Student("陈七11", R.mipmap.ic_launcher)); jsonStu1.add(new Student("孙八11", R.mipmap.ic_launcher)); jsonStu1.add(new Student("猴子搬来的救兵11http://blog.csdn.net/mynameishuangshuai", R.mipmap.ic_launcher)); // 通过回调方式传递数据 if (listener != null) { listener.onComplete(jsonStu1); } } }12345678910111213141516171819202122232425
添加Presenter层
public class StudentPresenterOne { // Model IStudentModel mStudentModel = new StudentModelImplOne(); // View IStudentView mStudentView; // 初始化View public StudentPresenterOne(IStudentView mStudentView) { this.mStudentView = mStudentView; } public void fetch(){ mStudentView.showLoading();// 显示进度 // Model获取数据 if (mStudentModel != null){ mStudentModel.loadStudent(new IStudentModel.StudentOnLoadListener() { @Override public void onComplete(List<Student> students) { // 得到数据后给View显示数据 mStudentView.showStudents(students); } }); } } }1234567891011121314151617181920212223
执行操作
/** * 功能:MainActivity 用列表形式实现 * 作者:猴子搬来的救兵 * 博客地址:http://blog.csdn.net/mynameishuangshuai * 日期:2016/10/01 */public class MainActivity extends AppCompatActivity implements IStudentView{ ListView myList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myList = (ListView) findViewById(R.id.my_list); new StudentPresenterOne(this).fetch(); } @Override public void showLoading() { Toast.makeText(this, "正在加载数据中……", Toast.LENGTH_SHORT).show(); } @Override public void showStudents(List<Student> students) { // 负责显示 myList.setAdapter(new StudentAdapter(MainActivity.this,students)); } }12345678910111213141516171819202122232425262728
显示结果
通过MVP框架实现View改变
实现新的Model层
public class StudentModelImplTwo implements IStudentModel { @Override public void loadStudent(StudentOnLoadListener listener) { Log.i("castiel","执行了StudentModelImplTwo数据加载"); // 模拟网络加载延时数据 SystemClock.sleep(1000); //模拟Json数据 List<Student> jsonStu2 = new ArrayList<Student>(); jsonStu2.add(new Student("张三22", R.mipmap.ic_launcher)); jsonStu2.add(new Student("李四22", R.mipmap.ic_launcher)); jsonStu2.add(new Student("王五22", R.mipmap.ic_launcher)); jsonStu2.add(new Student("赵六22", R.mipmap.ic_launcher)); jsonStu2.add(new Student("陈七22", R.mipmap.ic_launcher)); jsonStu2.add(new Student("孙八22", R.mipmap.ic_launcher)); jsonStu2.add(new Student("猴子搬来的救兵22http://blog.csdn.net/mynameishuangshuai", R.mipmap.ic_launcher)); // 通过回调方式传递数据 if (listener != null) { listener.onComplete(jsonStu2); } } }123456789101112131415161718192021222324
实现新的Presenter层
public class StudentPresenterTwo { // Model IStudentModel mStudentModel = new StudentModelImplTwo(); // View IStudentView mStudentView; // 初始化View public StudentPresenterTwo(IStudentView mStudentView) { this.mStudentView = mStudentView; } public void fetch(){ mStudentView.showLoading();// 显示进度 // Model获取数据 if (mStudentModel != null){ mStudentModel.loadStudent(new IStudentModel.StudentOnLoadListener() { @Override public void onComplete(List<Student> students) { // 得到数据后给View显示数据 mStudentView.showStudents(students); } }); } } }123456789101112131415161718192021222324
添加新的Activity,改变列表布局为网格布局
/** * 功能:TwoActivity 用网格形式实现 * 作者:猴子搬来的救兵 * 博客地址:http://blog.csdn.net/mynameishuangshuai * 日期:2016/10/01 */public class TwoActivity extends AppCompatActivity implements IStudentView{ GridView myGrid; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_two); myGrid = (GridView) findViewById(R.id.my_grid); new StudentPresenterTwo(this).fetch(); } @Override public void showLoading() { Toast.makeText(this, "正在加载数据中……", Toast.LENGTH_SHORT).show(); } @Override public void showStudents(List<Student> students) { // 负责显示 myGrid.setAdapter(new StudentGridAdapter(TwoActivity.this,students)); } }12345678910111213141516171819202122232425262728
新的Adapter
public class StudentGridAdapter extends BaseAdapter { private LayoutInflater myInflater; private List<Student> data; public StudentGridAdapter(Context context, List<Student> data) { myInflater = LayoutInflater.from(context); this.data = data; } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View stuView = myInflater.inflate(R.layout.stu_item, null); Student student = data.get(position); ImageView imgStu = (ImageView) stuView.findViewById(R.id.img_stu); imgStu.setImageResource(student.getStuImg()); TextView tvStu = (TextView) stuView.findViewById(R.id.tv_name); tvStu.setText(student.getName()); return stuView; } }12345678910111213141516171819202122232425262728293031323334353637
执行结果
共同学习,写下你的评论
评论加载中...
作者其他优质文章