现在很多 Android 开发都在用 MVP 模式,并且现在 Dagger2 注入依赖也挺方便的,所以就简单的对两者做了个封装,这样使用起来会更加方便。我们这里不对 MVP 做具体的讲解,只是简单的封装一下,便于使用,具体的 MVP 使用请参考其他文章。
代码已上传到 Github,有兴趣的可以去下载运行。
准备工作
Dagger引入
1compile 'com.google.dagger:dagger:2.10'2compile 'com.google.dagger:dagger-android-support:2.10' // if you use the support libraries3annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
M层
Model 层主要的职责有:
-
从网络,数据库,文件,传感器,第三方等数据源读写数据。
-
对外部的数据类型进行解析转换为APP内部数据交由上层处理。
-
对数据的临时存储,管理,协调上层数据请求。
我们首先来定义接口 IModel :
1public interface IModel {2 void onDestroy();3}
创建接口 BaseModel 继承于 IModel:
1public interface BaseModel extends IModel{2}
这里在 IModel 中定义了 onDestroy() 方法,我们知道,M 层主要是负责对数据进行操作的,我们难免会持有网络请求等其他的对象的引用,为了防止内存泄漏,我们需要在 Model 销毁的时候,通过 onDestroy()方法中释放持有的引用。
V层
在 MVP 开发中,View 层通常指的是 Activity 、Fragment、View、ViewGroup 等。
主要职责:
-
提供 UI 交互
-
在 Presenter 的控制下修改 UI。
-
将业务事件交由 Presenter 处理
View 层持有 Presenter 的引用,所以我们创建一个 BaseMvpActivity 如下:
1public abstract class BaseMvpActivity<P extends BasePresenter> extends AppCompatActivity { 2 3 @Inject 4 protected P mPresenter; 5 6 @Override 7 protected void onDestroy() { 8 super.onDestroy(); 9 if (null != mPresenter) {10 mPresenter.onDestroy();11 }12 this.mPresenter = null;13 }14}
可以看到,我们创建了个泛型 P ,并且这个泛型必须是 BasePresenter 的子类,可以看到定义 Presenter 的时候,上面有个注解是 @Inject ,这个注解就是 Dagger2 中的注解,使用这个注解就表示 mPresenter 是通过 Dagger2 注入的。另外在 Activity 销毁的时候,我们对持有的 Presenter 释放,防止内存泄漏。
然后我们再来定义一个接口 BaseView:
1public interface BaseView {2}
这个接口就代表 View 层,我们使用的时候,一般需要针对每个页面都有一个接口继承于 BaseView,然后需要在具体的 Activity 中实现新定义的这个接口(比如 public interface IMainView estends BaseView
,我们就要实现 IMainView 这个接口,而不是 BaseView 这个接口)。也就是说我们平时写的页面都要继承于 BaseMvpActivity 并且 实现 BaseView 接口的子类。比如:
1public interface IMainView extends BaseView{23}45public class MainActivity extends BaseMvpActivity implements IMainView{67}
P层
Presenter 层主要是连接 View 层和 Model 层的桥梁,负责把 View 层需要数据从 Model 层拿到,返回给 View 层;
所以我们在 P(Presenter) 层要持有 View 和 Model 层的引用。如下所示:
1public class BasePresenter<M extends BaseModel, V extends BaseView> implements IPresenter { 2 3 4 protected M mModel; 5 6 protected V mView; 7 8 public BasePresenter(M model, V view) { 9 mModel = model;10 mView = view;11 }1213 public BasePresenter(V view) {14 mView = view;15 }1617 @Override18 public void onDestroy() {19 if (mModel != null) {20 mModel.onDestroy();21 }22 this.mModel = null;23 this.mView = null;24 }25}
可以看到,我们分别持有 View 层的引用 mView,Model 层的引用 mModel,为了防止内存泄漏,我们定义了一个 Ipresenter 接口,
1public interface IPresenter {2 void onDestroy();3}
在 onDestroy() 方法中释放 View 层和 Model 层的引用。
使用
现在我们基本的已经封装好了,那么下面就通过一个实例来试用下:
创建 Component
1@Component(modules = Login1Module.class)2public interface Login1Component {3 void inject(Login1Activity login1Activity);4}
创建 Module
1@Module 2public class Login1Module { 3 4 private Login1Contract.ILoginView mILoginView; 5 6 public Login1Module(Login1Contract.ILoginView ILoginView) { 7 mILoginView = ILoginView; 8 } 910 @Provides11 Login1Contract.ILoginView getView() {12 return mILoginView;13 }1415 @Provides16 Login1Contract.ILoginModel getModel(Login1Model model) {17 return model;18 }19}
创建契约来规定 Model 和 View
分析 View 层和 Model 层需要的操作,定义接口如下:
1/** 2 * 定义 View 和 Model 层规则 3 * Created by smartsean on 2018/1/10. 4 */ 5public interface Login1Contract { 6 interface ILoginModel extends BaseModel{ 7 void login(String username, String password, OnLoginListener loginListener); 8 } 910 interface ILoginView extends BaseView{1112 String getUserName();1314 String getPassword();1516 void clearUserName();1718 void clearPassword();1920 void showLoading();2122 void hideLoading();2324 void toMainActivity(UserInfoModel userInfoModel);2526 void showFailedError();27 }2829 interface OnLoginListener{30 void loginSuccess(UserInfoModel userInfoModel);3132 void loginFailed();33 }3435}
创建 Model
Model 层主要是对数据操作的,我们这里开启线程模拟登录操作:
1/** 2 * @author SmartSean Created on 2018/1/25 16:49. 3 */ 4 5public class Login1Model implements Login1Contract.ILoginModel { 6 7 @Inject 8 public Login1Model() { 9 }1011 @Override12 public void login(final String username, final String password, final Login1Contract.OnLoginListener loginListener) {13 new Thread() {14 @Override15 public void run() {16 super.run();17 try {18 Thread.sleep(1000);19 } catch (InterruptedException e) {20 e.printStackTrace();21 }22 if ("sean".equals(username) && "2".equals(password)) {23 UserInfoModel userInfoModel = new UserInfoModel();24 userInfoModel.setUsername(username);25 userInfoModel.setPassword(password);26 loginListener.loginSuccess(userInfoModel);27 } else {28 loginListener.loginFailed();29 }30 }31 }.start();32 }3334 @Override35 public void onDestroy() {36 // TODO: 2018/1/25 销毁持有的引用37 }38}
创建 Presenter
Presenter 层主要是连接 View 层和 Model 层的桥梁:
1public class Login1Presenter extends BasePresenter<Login1Contract.ILoginModel, Login1Contract.ILoginView> { 2 3 @Inject 4 public Login1Presenter(Login1Contract.ILoginModel model, Login1Contract.ILoginView view) { 5 super(model, view); 6 } 7 8 private Handler mHandler = new Handler(); 910 /**11 * 模拟登陆12 */13 public void login() {14 mView.showLoading();15 mModel.login(mView.getUserName(), mView.getPassword(), new Login1Contract.OnLoginListener() {16 @Override17 public void loginSuccess(final UserInfoModel userInfoModel) {18 mHandler.post(new Runnable() {19 @Override20 public void run() {21 mView.toMainActivity(userInfoModel);22 mView.hideLoading();23 }24 });25 }2627 @Override28 public void loginFailed() {29 mHandler.post(new Runnable() {30 @Override31 public void run() {32 mView.showFailedError();33 mView.hideLoading();34 }35 });36 }37 });38 }394041 @Override42 public void onDestroy() {43 super.onDestroy();44 // TODO: 2018/1/25 销毁持有的引用45 }4647 /**48 * 清除账户名和密码49 */50 public void clear() {51 mView.clearUserName();52 mView.clearPassword();53 }54}
创建Activity
1public class Login1Activity extends BaseMvpActivity<Login1Presenter> implements Login1Contract.ILoginView { 2 3 private EditText usernameEt, passwordEt; 4 private Button loginBtn, clearBtn; 5 private ProgressDialog progressDialog; 6 7 private Context mContext; 8 9 private static final String TAG = "Login1Activity";1011 @Override12 protected void onCreate(Bundle savedInstanceState) {13 super.onCreate(savedInstanceState);14 setContentView(R.layout.activity_login1);15 initView();16 mContext = this;17 DaggerLogin1Component.builder().login1Module(new Login1Module(this)).build().inject(this);18 }1920 private void initView() {21 usernameEt = findViewById(R.id.username_et);22 passwordEt = findViewById(R.id.password_et);23 loginBtn = findViewById(R.id.login_btn);24 clearBtn = findViewById(R.id.clear_btn);25 loginBtn.setOnClickListener(new View.OnClickListener() {26 @Override27 public void onClick(View view) {28 mPresenter.login();29 }30 });31 clearBtn.setOnClickListener(new View.OnClickListener() {32 @Override33 public void onClick(View view) {34 mPresenter.clear();35 }36 });37 progressDialog = new ProgressDialog(this);38 progressDialog.setTitle("加载中");39 }4041 @Override42 public String getUserName() {43 return usernameEt.getText().toString().trim();44 }4546 @Override47 public String getPassword() {48 return passwordEt.getText().toString().trim();49 }5051 @Override52 public void clearUserName() {53 usernameEt.setText("");54 }5556 @Override57 public void clearPassword() {58 passwordEt.setText("");59 }6061 @Override62 public void showLoading() {63 progressDialog.show();64 }6566 @Override67 public void hideLoading() {68 progressDialog.dismiss();69 }7071 @Override72 public void toMainActivity(UserInfoModel userInfoModel) {73 MainActivity.startAction(this, userInfoModel.getUsername());74 finish();75 }7677 @Override78 public void showFailedError() {79 Log.d(TAG, "showFailedError: 登陆失败");80 Toast.makeText(mContext, "登陆失败,请重新尝试", Toast.LENGTH_SHORT).show();81 }82}