前言 什么是MVP,为什么要用MVP? 网上介绍MVP的很多,百度一下你就知道,至于为什么要用MVP,当然是由于它的优势了:
代码易读
虽然这种模式增加了代码量,但是代码的可读性大大提升。
降低耦合,方便维护
MVP的使用,使Activity中的网络请求剥离出来 成为model、presenter。
MVP顾名思义 Model、View、Presenter
Model:负责网络的请求
View:进行界面的展示
Presenter:负责处理请求网络后的数据处理:加载中 成功 or 失败 取消加载
那Contract是什么用呢?
这就涉及到MVP的缺点了,MVP在实现代码易读的同时,额外增加了大量的接口、类,不方便进行管理,于是Contract就登场了。
Contract 如其名,是一个契约,将Model、View、Presenter 进行约束管理,方便后期类的查找、维护。
具体代码 依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //butterknife implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' //okhttp3 implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation "com.squareup.okhttp3:logging-interceptor:3.10.0" //retrofit2 implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' //rxjava2 implementation "io.reactivex.rxjava2:rxjava:2.1.12" implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' //AutoDispose解决RxJava内存泄漏 implementation 'com.uber.autodispose:autodispose:0.7.0' implementation 'com.uber.autodispose:autodispose-android:0.7.0' implementation 'com.uber.autodispose:autodispose-android-archcomponents:0.7.0'
基类 BaseActivity 如果使用Kotlin及其插件可以不用butterknife
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import android.arch.lifecycle.Lifecycle;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import com.uber.autodispose.AutoDispose;import com.uber.autodispose.AutoDisposeConverter;import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider;import butterknife.ButterKnife;import butterknife.Unbinder;public abstract class BaseActivity extends AppCompatActivity { private Unbinder unbinder; @Override protected void onCreate (@Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(this .getLayoutId()); unbinder = ButterKnife.bind(this ); initView(); } @Override protected void onDestroy () { unbinder.unbind(); super .onDestroy(); } public abstract int getLayoutId () ; public abstract void initView () ; }
BaseFragment 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import android.app.Fragment;import android.os.Bundle;import android.support.annotation.Nullable;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import butterknife.ButterKnife;import butterknife.Unbinder;public abstract class BaseFragment extends Fragment { private Unbinder unBinder; @Override public void onCreate (@Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); } @Nullable @Override public View onCreateView (LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(this .getLayoutId(), container, false ); unBinder = ButterKnife.bind(this , view); initView(view); return view; } @Override public void onDestroyView () { super .onDestroyView(); unBinder.unbind(); } protected abstract void initView (View view) ; protected abstract int getLayoutId () ; }
BaseView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import com.uber.autodispose.AutoDisposeConverter; public interface BaseView { void showLoading () ; void hideLoading () ; void onError (Throwable throwable ) ; <T> AutoDisposeConverter<T> bindAutoDispose () ; }
BasePresenter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class BasePresenter <V extends BaseView > { protected V mView; public void attachView (V view) { this .mView = view; } public void detachView () { this .mView = null ; } public boolean isViewAttached () { return mView != null ; } }
BaseMvpActivity 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import android.arch.lifecycle.Lifecycle;import android.os.Bundle;import android.support.annotation.Nullable;import com.uber.autodispose.AutoDispose;import com.uber.autodispose.AutoDisposeConverter;import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider;public abstract class BaseMvpActivity <T extends BasePresenter > extends BaseActivity implements BaseView { protected T mPresenter; @Override protected void onCreate (@Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); } @Override protected void onDestroy () { if (mPresenter != null ) { mPresenter.detachView(); } super .onDestroy(); } @Override public <T> AutoDisposeConverter<T> bindAutoDispose () { return AutoDispose.autoDisposable(AndroidLifecycleScopeProvider .from(this , Lifecycle.Event.ON_DESTROY)); } }
BaseMvpFragment 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import android.arch.lifecycle.Lifecycle;import com.uber.autodispose.AutoDispose;import com.uber.autodispose.AutoDisposeConverter;import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider;public abstract class BaseMvpFragment <T extends BasePresenter > extends BaseFragment implements BaseView { protected T mPresenter; @Override public void onDestroyView () { super .onDestroyView(); if (mPresenter != null ) { mPresenter.detachView(); } super .onDestroyView(); } @Override public <T> AutoDisposeConverter<T> bindAutoDispose () { return AutoDispose.autoDisposable(AndroidLifecycleScopeProvider .from(this , Lifecycle.Event.ON_DESTROY)); } }
线程调度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import org.reactivestreams.Publisher;import io.reactivex.Flowable;import io.reactivex.FlowableTransformer;import io.reactivex.Observable;import io.reactivex.ObservableSource;import io.reactivex.ObservableTransformer;import io.reactivex.android.schedulers.AndroidSchedulers;import io.reactivex.annotations.NonNull;import io.reactivex.schedulers.Schedulers;public class RxScheduler { public static <T> FlowableTransformer<T, T> Flo_io_main () { return new FlowableTransformer <T, T>() { @Override public Publisher<T> apply (@NonNull Flowable<T> upstream) { return upstream.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }; } public static <T> ObservableTransformer<T, T> Obs_io_main () { return new ObservableTransformer <T, T>() { @Override public ObservableSource<T> apply (Observable<T> upstream) { return upstream.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }; } }
登录模块 LoginContract 首先,创建一个登陆的Contract:
1 2 3 4 5 public interface LoginContract { interface Model { } interface View extends BaseView { } interface Presenter { } }
其次创建Presenter、Model、View 对应Contract中的接口;
1 2 3 public class LoginPresenter implements LoginContract.Presenter{} public class LoginModel implements LoginContract.Model{} public class LoginActivity implements LoginContract.View {}
具体代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public interface LoginContract { interface Model { Flowable<BaseObjectBean<LoginBean>> login (String username, String password) ; } interface View extends BaseView { @Override void showLoading () ; @Override void hideLoading () ; @Override void onError (Throwable throwable) ; void onSuccess (BaseObjectBean<LoginBean> bean) ; } interface Presenter { void login (String username, String password) ; } }
在LoginContract 中
Model接口 创建对应的联网请求的方法,将Presenter提交的字段放到联网请求中,发送给服务器
View 接口 创建在界面上显示加载中、取消加载以及登陆成功、失败的方法
Presenter 接口 创建 登陆的方法,以及需要提交的字段 (username、password)
LoginModel 1 2 3 4 5 6 7 8 import io.reactivex.Flowable;public class LoginModel implements LoginContract .Model { @Override public Flowable<BaseObjectBean<LoginBean>> login (String username, String password) { return RetrofitClient.getInstance().getApi().login(username,password); } }
LoginPresenter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class LoginPresenter extends BasePresenter <LoginContract.View> implements LoginContract .Presenter { private LoginContract.Model model; public LoginPresenter () { model = new LoginModel (); } @Override public void login (String username, String password) { if (!isViewAttached()) { return ; } mView.showLoading(); model.login(username, password) .compose(RxScheduler.Flo_io_main()) .as(mView.bindAutoDispose()) .subscribe(new Consumer <BaseObjectBean<LoginBean>>() { @Override public void accept (BaseObjectBean<LoginBean> bean) throws Exception { mView.onSuccess(bean); mView.hideLoading(); } }, new Consumer <Throwable>() { @Override public void accept (Throwable throwable) throws Exception { mView.onError(throwable); mView.hideLoading(); } }); } }
LoginPresenter 实现LoginContract.Presenter 接口中的 login(String username, String password) 方法
实例化Model,在LoginPresenter login(String username, String password)方法中,调用model的网络请求,将username、password放在model的login()方法中,进行请求服务器。 请求服务器前 使用LoginContract.View中的 mView.showLoading()方法,进行显示加载中;在成功失败的回调中,使用对应的方法,以及取消加载。
其中BasePresenter、BaseView 是对Presenter以及View进行的封装
LoginActicity 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public class LoginActivity extends BaseMvpActivity <LoginPresenter> implements LoginContract .View { @BindView(R.id.et_username_login) TextInputEditText etUsernameLogin; @BindView(R.id.et_password_login) TextInputEditText etPasswordLogin; @Override public int getLayoutId () { return R.layout.activity_Login; } @Override public void initView () { mPresenter = new LoginPresenter (); mPresenter.attachView(this ); } private String getUsername () { return etUsernameLogin.getText().toString().trim(); } private String getPassword () { return etPasswordLogin.getText().toString().trim(); } @Override public void onSuccess (BaseObjectBean bean) { Toast.makeText(this , bean.getErrorMsg(), Toast.LENGTH_SHORT).show(); } @Override public void showLoading () { ProgressDialog.getInstance().show(this ); } @Override public void hideLoading () { ProgressDialog.getInstance().dismiss(); } @Override public void onError (Throwable throwable) { } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); } @OnClick(R.id.btn_signin_login) public void onViewClicked () { if (getUsername().isEmpty() || getPassword().isEmpty()) { Toast.makeText(this , "帐号密码不能为空" , Toast.LENGTH_SHORT).show(); return ; } mPresenter.login(getUsername(), getPassword()); } }
LoginActivity 中实现 LoginContract.View中的方法 ,在实现的方法中,进行进度条加载、和登陆成功or失败的UI的展示:
1 2 3 4 5 6 7 8 9 10 @Override void showLoading () ;@Override void hideLoading () ;@Override void onError (Throwable throwable) ;void onSuccess (BaseObjectBean<LoginBean> bean) ;
MVPHelper MVP快速生成类的插件:https://github.com/githubwing/MVPHelper
该插件主要是为了生成Contract、 Model和Presenter
使用快捷键 Alt + Insert , 点击MvpHelper,自动生成文件
注意
只支持Java,不支持Kotlin
可以对后缀为Contract或Presenter的Java源文件中进行生成,
Contract所处的位置中必须有至少一个包以 contract 结尾,
Presenter所处的位置中必须有至少一个包以 presenter 结尾,否则生成报错
重复生成将删除旧文件 并重新生成,所以不要在已写代码后重新生成,会导致代码丢失
强制 Contract 为 interface
Contract 中的三个接口不包含多余的修饰符
作用就是
在contract内创建如下接口
1 2 public interface GoodsInfoContract {}
该文件会自动生成如下格式
1 2 3 4 5 6 7 8 9 10 public interface GoodsInfoContract { interface Model { } interface View { } interface Presenter { } }
会自动创建如下文件
model下
1 2 public class GoodsInfoModel implements GoodsInfoContract.Model { }
presenter下
1 2 public class GoodsInfoPresenter implements GoodsInfoContract .Presenter {}
或者
使用Presenter生成
1 2 public class GoodsInfoPresenter {}
该文件会自动生成如下格式
1 2 public class GoodsInfoPresenter implements GoodsInfoContract .Presenter {}
同时生成其它两个文件
相关