前言
首先如果你是一个半年以内的安卓初学者,如果你对逻辑思维的感受不够强烈的话,我不建议你立马接触MVP设计模式。为什么呢?因为我自己就是这波人里面的…尴尬个三秒钟,跳过。这种东西具体是什么呢?
身边其实有很多类似的例子,如果非要举个例子,我个人感觉跟盖房子有那么一丢丢类似,但又不完全类似。
以一个大型公司为例,它会分很多的部门,然后每个部门具有一定的职能,但其实很多公司的部门架构都是具有一定的相似性的,这也就是我们通常说的模式化~而每个人做的事情也许别人也能做,就好比Activity里面你也可以做联网操作,也可以做View操作,但是你啥事儿都让他一个做是不是有点累呢?所以,合理的结构与分工可以让整个综合体更有效率的运行与维护。
在此之前,我希望初学者不是抱着一口吃个胖子的心态去接触这种设计模式。由更浅到浅,会让你理解更明白。在学习MVP以前,我强烈建议先学习MVC【标准MVC不是那种不三不四的结构的Demo或者渣渣项目】,把这种分模块与分层的思想带到你的项目中。这里是我的MVC学习笔记:http://www.jianshu.com/p/894420ac8fdc
不懂的哥们姐们多写几遍,一定不要懒~
入门
首先对于网上的那些理论性的玩意儿,我不想复制粘贴过来。一个原因,太抽象不具体。我们很难理解那些看不到的玩意儿。
如果你学会了MVC,那你应该就会有一种体会,就是你的联网操作,数据操作,以及一些耗时的操作等都是写在Model中的,然后你把数据塞到要显示的View中的时候,都是在Activity或Fragment等等这些Java文件中的,那么这么做的好处是啥?没有好处我们肯定不会用它的对吧,我想很多人刚开始学习安卓的时候,写demo,例如写一个显示天气数据的Activity,肯定你View也写在里头,联网也写在里头,然后第一步怎么联网,第二步回调怎么写数据,第三部怎么显示消失动画等等…其实这也没啥,但是你啥都写在一个里面,你一个Activity要写两千多行咩?协同开发的时候是不是给别人一种坑的感觉咩?
同样是写个显示天气数据的demo,使用MVC,你的View相关的操作,全部是在Activity中的,而你的联网等全部是在Model中的,这样的话,我们就吧视图【你眼睛看到的,手摸到的】与联网【比较耗时间的需要异步的】,这两种行为用代码隔开了。对于单个文件来说,降低代码量,易于维护。对于模块式的开发形式来说,独立的东西往往比黏合在一起的东西好控制,也就是我们常说的解耦。
然后,在我学会MVC以后我再写MVP就很容易明白它的目的了。首先MVC我们已经清楚,M是做耗时操作与业务逻辑处理的,View是我们眼睛看到的,你就把它当作布局文件就好不要想太复杂。然后C就是controller,就是我们把布局文件连接起来的,那不就是我们的Activity嘛。然后,MVP则相当于进化版本的MVC。我们知道,如果你要把一行文字塞到TextView中,要使用的是setText的方法,这个方法是针对TextView这个View的操作,而你塞进去,这一动作,我们要把它独立出来看,比如我吃饭,我的吃,与饭进到嘴巴里面,综合来看是一个行为动作,但是这里呢就把它进一步独立开,吃是逻辑,而饭进到嘴巴里才是针对你的嘴巴的操作。
以上逻辑的解释如果你还看不懂,哥哥我就不好说啥了…
代码【demo参照:http://blog.csdn.net/knxw0001/article/details/39637273/ 进行了少量修改】
我们要实现一个这样的功能:可以从EditText读取用户信息并存取,也可以根据ID来从后台读出用户信息并显示在EditText中。
首先来看一下分包
看一下布局:
布局文件的相关代码我就不贴上来了比较简单
然后看上面那个分包的图,首先如果你会MVC,你就知道Model的作用是放置耗时的操作与业务逻辑的模块,在这个demo中,Model要做的事情是什么?主要做的工作是,把从View中拿到的数据存起来,以及从存入的地方读出来并且拿出来使用。
UserBean
public class UserBean {
public UserBean(String first_name, String last_name) {
this.first_name = first_name;
this.last_name = last_name;
}
private String first_name;
private String last_name;
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
}
看一下UserModel接口以及它的实现类UserModelImp,如果你不知道为啥子要写个实现类,建议你先去把MVC学会并且使用熟练。
UserModel
相关备注:UserModel主要做的工作是,把从View中拿到的数据存起来,以及从存入的地方读出来给别的地方用
public interface UserModel {
void setId(int id);
void setFirstName(String first_name);
void setLastName(String last_name);
UserBean getUserInfo(int id);//通过id获取first_name与last_name,返回一个UserBean
}
UserModelImp
相关备注:实现UserModel接口,来把具体要做的工作放在这里
public class UserModelImp implements UserModel {
private int id;
private String first_name;
@Override
public void setId(int id) {
this.id = id;
}
@Override
public void setFirstName(String first_name) {
if (first_name != null && first_name.length() > 0) {
this.first_name = first_name;
} else {
Toast.makeText(MainApplication.MainContext, "首次姓名不能为空", Toast.LENGTH_SHORT).show();
}
}
@Override
public void setLastName(String last_name) {
if (last_name != null && last_name.length() > 0) {
MainApplication.userArray.append(id, new UserBean(first_name, last_name));
Toast.makeText(MainApplication.MainContext, "存入成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainApplication.MainContext, "最后姓名不能为空", Toast.LENGTH_SHORT).show();
}
}
@Override
public UserBean getUserInfo(int id) {
return MainApplication.userArray.get(id);
}
}
那么在这个例子中的MVP的M部分已经完成,然后要看一下P,P是prensneter,意思是表现,展现出来,它很类似于controller,还以吃饭为例。如果controller表示吃饭这个整体动作,那么presenter就是拆分出来的吃,而View就是把饭送到嘴巴里的动作。
然后我们要先把饭送到嘴巴里才能吃对不对?看一下UserView接口。
UserView
相关备注:UserView可以做的工作是,从EditText塞入或读取数据,即与view的交互
PS:对于View而言,它主要是面对View所做的操作,在本例中,对View的操作主要是从从EditText塞入或读取数据,然后与Button之间存在一定的交互
public interface UserView {
int getId();
String getFirstName();
String getLastName();
void setFirstName(String first_name);
void setLastName(String last_name);
void clearText();
}
然后是吃的逻辑,也就是presenter
UserPresenter
相关备注:Presenter主要起到了一个连接器的作用,也就是承载了大量原来在Activity中的操作
PS:以本例子的需求为例,主要要做两方面的操作,一方面是存,一方面是取,那么要在这里把View和Model的作用发挥出来
public class UserPresenter {
private UserView userView;
private UserModel userModel;
/*这里需要把View的实现类传入*/
public UserPresenter(UserView userView) {
this.userView = userView;
this.userModel = new UserModelImp();//这里是实现类
}
/*存的操作*/
public void saveUserInfo(int id, String first_name, String last_name) {
if (id != 0) {
userModel.setId(id);
userModel.setFirstName(first_name);
userModel.setLastName(last_name);
} else {
Toast.makeText(MainApplication.MainContext, "id不能为0或为空", Toast.LENGTH_SHORT).show();
}
userView.clearText();
}
/*取的操作*/
public void readUserInfo(int id) {
try {
if (id != 0) {
userView.setFirstName(userModel.getUserInfo(id).getFirst_name());
userView.setLastName(userModel.getUserInfo(id).getLast_name());
} else {
throw new Exception();
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(MainApplication.MainContext, "未查询到此数据", Toast.LENGTH_SHORT).show();
}
}
}
以上关于view和presenter的部署已经完成,接下来就要在Activity中来使用了。
MainActivity
public class MainActivity extends AppCompatActivity implements UserView, View.OnClickListener {
private EditText etUserID;
private EditText etUserFirstName;
private EditText etUserLastName;
private Button btnSave;
private Button btnRead;
private UserPresenter userPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
initView();
userPresenter = new UserPresenter(this);
}
private void initView() {
etUserID = (EditText) findViewById(R.id.etUserID);
etUserFirstName = (EditText) findViewById(R.id.etUserFirstName);
etUserLastName = (EditText) findViewById(R.id.etUserLastName);
btnSave = (Button) findViewById(R.id.btnSave);
btnRead = (Button) findViewById(R.id.btnRead);
btnSave.setOnClickListener(this);
btnRead.setOnClickListener(this);
}
@Override//此方法里没有完全隔离【后期修改】
public int getId() {
if (etUserID.getText().toString().length() > 0) {
return Integer.parseInt(etUserID.getText().toString());
} else {
return 0;
}
}
@Override
public String getFirstName() {
return etUserFirstName.getText().toString();
}
@Override
public String getLastName() {
return etUserLastName.getText().toString();
}
@Override
public void setFirstName(String first_name) {
etUserFirstName.setText(first_name);
}
@Override
public void setLastName(String last_name) {
etUserLastName.setText(last_name);
}
@Override
public void clearText() {
etUserID.setText("");
etUserFirstName.setText("");
etUserLastName.setText("");
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSave:
userPresenter.saveUserInfo(getId(), getFirstName(), getLastName());
break;
case R.id.btnRead:
userPresenter.readUserInfo(getId());
break;
}
}
}
程序入口:MainApplication
public class MainApplication extends Application {
public static SparseArray<UserBean> userArray = new SparseArray<>();
public static Context MainContext;
@Override
public void onCreate() {
super.onCreate();
MainContext = this;
}
}
可以看到,Activity里已经看不到Model的影子了,Model和View之间的交互全部放在了Presenter里面,很好的隔离了耗时操作了联网等与Activity黏性过高的问题,因为这个例子很经典,也很简单,可以比作一个简单化的登陆demo,但是这个例子还有不足的地方。有时候一个Activity里我们可能会做更多复杂的操作与逻辑,这样的情况下我们可能会写多个Presenter与Model,合理分包,合理的放置逻辑,都是为了实现模块化,独立化的开发思想。
演示效果
其他
对于开发来说,有几个比较重要的个人过程。一个是基本理论,理论你都不懂怎么能让程序正常的跑起来?第二个就是思维,思维影响效率,比如有人做家务,他会先做饭,做完饭再扫地,扫完地再洗衣服,可能有的人就是饭正在烧的时候,他打开了洗衣机洗衣服,然后同时去扫地。这就是思维方式的差异,我希望通过最浅显易懂的方式,来把复杂的问题解释清楚,当然如果实在无法理解,也许就是个人天生的思维逻辑不够完善。