一、首先咱们来很不情愿的简单介绍一下
MVP
这个东东,为什么说不情愿,首先,这些概念性的东西我本身都有点看不下去,其次网上讲这个的东西实在是太多了。官方的解释为经典MVC
模式的演化版,这里咱们也不详细讲啥是MVC
了,就是一种古老而又神奇的模式。讲一个很简单的栗子:有一家早餐店,他们家卖包子、馒头、油条、粥等等这些东西,他们须要用到最重要的东西是面粉(原材料:大米),每次早餐店老板都从一家面粉店(大米店)订购,而这些大米都是面粉店老板去农民伯伯那里收购的。在这个小例子当中,农民伯伯的任务就是生产出大米,这其实就至关于
MVP
模式中的Model
,大米就是实体,生产大米就是业务的逻辑;面粉店老板负责收购大米而后加工成面粉,这其实就至关于MVP
模式中的Presenter
,负责完成农民伯伯和早餐店老板这两边的间接交易;最后早餐店老板的任务就是负责将面粉作成各类各样的早餐供你们享用,这其实就至关于MVP
模式中的View
,负责将面粉作成各类各样的食物呈如今你们眼前。好了,相信经过这个栗子你们应该对MVP
模式有必定的了解了,下面来看一张图:php
二、相信用过
MVP
的同志们都有体会,每一个人对于MVP
模式的理解都不同,这样致使写出来的代码也都风格迥异,可是思想都是同样的(即下降Model
和View
之间的耦合度,使得代码变的更清晰),因此即将学习MVP
的小伙伴们看博客的时候不要惊慌,由于大家会看到各类的代码,下面咱们一块儿来看一下LZ写的(写的很差的地方欢迎指正):(1)、首先咱们来看一下
Model
,这部分我理解的就是数据得到的地方,换句话说就是进行网络请求的地方(或者本地数据的获取),这里我写了一个Base
类,将全部的Model
请求数据相同的部分都放到了一块儿:java
public class BaseModel extends BaseRetrofit {
private static final String TAG = "BaseModel";
protected CygApi mServletApi; //全部的注解接口
protected Map<String, String> mParams = new HashMap<>();
public BaseModel() {
super();
mServletApi = mRetrofit.create(CygApi.class);
}
//获取公共参数
@Override
protected Map<String, String> getCommonMap() {
Map<String, String> commonMap = new HashMap<>();
commonMap.put("user_id", String.valueOf(UserDao.getInstance().getUserId()));
commonMap.put("token", String.valueOf(UserDao.getInstance().getToken()));
return commonMap;
}
//添加一个参数
protected void addParams(String key, String value) {
if (TextUtils.isEmpty(key)) {
Log.e(TAG, "the key is null");
return;
}
mParams.put(key, value);
}
//添加多个参数
protected void addParams(Map<String, String> params) {
if (null == params) {
Log.e(TAG, "the map is null");
return;
}
mParams.putAll(params);
}
}复制代码
这里网络请求用的是Retrofit+RxJava,这一部分我打算放到下一篇再讲,在这个Base类里面主要添加了公共参数和添加普通参数。来看看一个登陆的
Model
:mysql
public class LoginModel extends BaseModel {
public static LoginModel getInstance() {
return getPresent(LoginModel.class);
}
public void execute(String phone, String password, Observer<User> observer) {
addParams("username", phone);
addParams("password", password);
Observable observable = mServletApi.getUserInfo(mParams).map(new HttpFunction());
toSubscribe(observable, observer);
}
}复制代码
(2)、这里我是用的单例模式(其实我也不知道这样写单例会不会有错,用类去实例化一个对象>_<我以为没啥问题,哈哈哈),而后
execute
方法就是进行网络请求了,在Presenter
中调用这个方法就好了。而后咱们来看一下BasePresenter
:android
public class BasePresenter<VIEW> {
private WeakReference<VIEW> mViews;
protected void attachView(VIEW view) {
mViews = new WeakReference<VIEW>(view);
}
protected VIEW getView() {
return isViewAttached() ? mViews.get() : null;
}
private boolean isViewAttached() {
return null != mViews && null != mViews.get();
}
protected void detachView() {
if (null != mViews) {
mViews.clear();
mViews = null;
}
}
}复制代码
在
BasePresenter
里面我只是关注了View
,按照MVP
模式的理解,咱们应该在这个里面同时关注View
和Model
,确实,不少demo都是这样干的,可是LZ
前面是用的单例来写Model
,因此在BasePresenter
里面就暂时先关注View
,还有一点须要说明的是,这里对View
使用的弱引用,咱们都知道View
一般来讲都是很大只的存在,为了防止内存泄漏,使用弱引用来及时释放内存。来看看一个登陆的LoginPresenter
:git
public class LoginPresenter extends BasePresenter<LoginView<User>> {
public LoginPresenter(LoginView<User> loginView) {
attachView(loginView);
}
public void getUserInfo(BaseImpl baseImpl) {
LoginModel.getInstance().execute(getView().getUserName(), getView().getPassword(), new CygBaseObserver<User>(baseImpl, "正在登陆") {
@Override
protected void onBaseNext(User data) {
UserInfo userInfo = new UserInfo();
userInfo.setId(data.getId());
userInfo.setUsername(getView().getUserName());
userInfo.setToken(data.getToken());
UserDao.getInstance().deleteAll(UserInfo.class);
UserDao.getInstance().insertObject(userInfo);
getView().onRequestSuccessData(data);
}
});
}
public void toMainActivity(Activity activity) {
activity.startActivity(new Intent(activity, MainActivity.class));
}
}复制代码
(3)、在登陆的
LoginPresenter
中调用LoginModel
进行网络请求,只返回一个成功的回调,失败的回调咱们在内部处理掉了,而后在回调成功以后作相应的数据操做(该回调给View
的就回调给View
,该存本地的就存本地)。而后来看看咱们的View
:github
public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView<User> {
@BindView(R.id.al_et_user_name)
TextInputEditText alEtUserName;
@BindView(R.id.al_et_password)
TextInputEditText alEtPassword;
@Override
protected int layoutRes() {
return R.layout.activity_login;
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter(this);
}
@Override
protected void initView() {
}
@Override
public String getUserName() {
return alEtUserName.getText().toString().trim();
}
@Override
public String getPassword() {
return alEtPassword.getText().toString().trim();
}
@OnClick(R.id.al_btn_login)
public void onViewClicked() {
if (TextUtils.isEmpty(getUserName())) {
alEtPassword.setError("用户名不能为空");
return;
}
if (TextUtils.isEmpty(getPassword())) {
alEtPassword.setError("密码不能为空");
return;
}
mPresenter.getUserInfo(this);
}
@Override
public void onBackPressed() {
super.onBackPressed();
moveTaskToBack(true);
}
@Override
public void onRequestSuccessData(User data) {
mPresenter.toMainActivity(this);
}
}复制代码
在
View
中,就是初始化Presenter
,而后各类调Presenter
中的方法,这里原本是能够在Presenter
中直接调用toMainActivity()
方法的,为了演示成功回调以后再回调给View
,这里我就多作了一步操做。好了,接下来咱们来看看接口:sql
public interface BaseRequestContract<T> {
void onRequestSuccessData(T data);
}复制代码
这里写了一个
Base
接口,因为大多时候咱们只关注成功的回调数据,这里我也只写了一个成功回调的方法(若是你有其余的需求,你能够在这里加一些公共的方法),若是你有须要的话你能够在子类中写错误回调的接口,接着咱们来看看登陆的接口有哪些方法:后端
public interface LoginView<T> extends BaseRequestContract<T>{
String getUserName();
String getPassword();
}复制代码
这里我须要得到用户的输入信息,因此只简单定义了两个方法用来获取用户名和密码。到这里咱们的
MVP
模式就简单封装的差很少了,接下来咱们来看一下最终的效果吧:设计模式
这里用
eclipse+tomcat+mysql
简单写了一个登陆接口,这一部分LZ
在以前的博客中有详细讲解,若有兴趣,请移步:tomcat
好了,MVP
的基本封装就讲到这里,下一节咱们再来说一下Retrofit+Rxjava
的简单封装及使用,这里先奉上代码:
可能不少人就会问了,为何会有两份呢,这里我给你们看看个人项目工程
LZ
把跟主项目无关的逻辑都写到module中去了,这样也是为了更好的重用代码。若是你直接去下载两个文件的话,那么请你下载以后把cygmodule-master
文件夹的名字改为cygmodule
,或者你也能够在主工程中将setting.gradle
中的String jackchengPath = rootDir.absolutePath + '/../cygmodule'
更换成你的路径