Dagger2 + MVP的简单封装
现在很多 Android 开发都在用 MVP 模式,并且现在 Dagger2 注入依赖也挺方便的,所以就简单的对两者做了个封装,这样使用起来会更加方便。我们这里不对 MVP 做具体的讲解,只是简单的封装一下,便于使用,具体的 MVP 使用请参考其他文章。
代码已上传到 Github,有兴趣的可以去下载运行。
准备工作
Dagger引入
compile 'com.google.dagger:dagger:2.10'
compile 'com.google.dagger:dagger-android-support:2.10' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
M层
Model 层主要的职责有:
从网络,数据库,文件,传感器,第三方等数据源读写数据。
对外部的数据类型进行解析转换为APP内部数据交由上层处理。
对数据的临时存储,管理,协调上层数据请求。
我们首先来定义接口 IModel :
public interface IModel {
void onDestroy();
}
创建接口 BaseModel 继承于 IModel:
public interface BaseModel extends IModel{
}
这里在 IModel 中定义了 onDestroy() 方法,我们知道,M 层主要是负责对数据进行操作的,我们难免会持有网络请求等其他的对象的引用,为了防止内存泄漏,我们需要在 Model 销毁的时候,通过 onDestroy()方法中释放持有的引用。
V层
在 MVP 开发中,View 层通常指的是 Activity 、Fragment、View、ViewGroup 等。
主要职责:
提供 UI 交互
在 Presenter 的控制下修改 UI。
将业务事件交由 Presenter 处理
View 层持有 Presenter 的引用,所以我们创建一个 BaseMvpActivity 如下:
public abstract class BaseMvpActivity<P extends BasePresenter> extends AppCompatActivity {
@Inject
protected P mPresenter;
@Override
protected void onDestroy() {
super.onDestroy();
if (null != mPresenter) {
mPresenter.onDestroy();
}
this.mPresenter = null;
}
}
可以看到,我们创建了个泛型 P ,并且这个泛型必须是 BasePresenter 的子类,可以看到定义 Presenter 的时候,上面有个注解是 @Inject ,这个注解就是 Dagger2 中的注解,使用这个注解就表示 mPresenter 是通过 Dagger2 注入的。另外在 Activity 销毁的时候,我们对持有的 Presenter 释放,防止内存泄漏。
然后我们再来定义一个接口 BaseView:
public interface BaseView {
}
这个接口就代表 View 层,我们使用的时候,一般需要针对每个页面都有一个接口继承于 BaseView,然后需要在具体的 Activity 中实现新定义的这个接口(比如 public interface IMainView estends BaseView
,我们就要实现 IMainView 这个接口,而不是 BaseView 这个接口)。也就是说我们平时写的页面都要继承于 BaseMvpActivity 并且 实现 BaseView 接口的子类。比如:
public interface IMainView extends BaseView{
}
public class MainActivity extends BaseMvpActivity implements IMainView{
}
P层
Presenter 层主要是连接 View 层和 Model 层的桥梁,负责把 View 层需要数据从 Model 层拿到,返回给 View 层;
所以我们在 P(Presenter) 层要持有 View 和 Model 层的引用。如下所示:
public class BasePresenter<M extends BaseModel, V extends BaseView> implements IPresenter {
protected M mModel;
protected V mView;
public BasePresenter(M model, V view) {
mModel = model;
mView = view;
}
public BasePresenter(V view) {
mView = view;
}
@Override
public void onDestroy() {
if (mModel != null) {
mModel.onDestroy();
}
this.mModel = null;
this.mView = null;
}
}
可以看到,我们分别持有 View 层的引用 mView,Model 层的引用 mModel,为了防止内存泄漏,我们定义了一个 Ipresenter 接口,
public interface IPresenter {
void onDestroy();
}
在 onDestroy() 方法中释放 View 层和 Model 层的引用。
使用
现在我们基本的已经封装好了,那么下面就通过一个实例来试用下:
创建 Component
@Component(modules = Login1Module.class)
public interface Login1Component {
void inject(Login1Activity login1Activity);
}
创建 Module
@Module
public class Login1Module {
private Login1Contract.ILoginView mILoginView;
public Login1Module(Login1Contract.ILoginView ILoginView) {
mILoginView = ILoginView;
}
@Provides
Login1Contract.ILoginView getView() {
return mILoginView;
}
@Provides
Login1Contract.ILoginModel getModel(Login1Model model) {
return model;
}
}
创建契约来规定 Model 和 View
分析 View 层和 Model 层需要的操作,定义接口如下:
/**
* 定义 View 和 Model 层规则
* Created by smartsean on 2018/1/10.
*/
public interface Login1Contract {
interface ILoginModel extends BaseModel{
void login(String username, String password, OnLoginListener loginListener);
}
interface ILoginView extends BaseView{
String getUserName();
String getPassword();
void clearUserName();
void clearPassword();
void showLoading();
void hideLoading();
void toMainActivity(UserInfoModel userInfoModel);
void showFailedError();
}
interface OnLoginListener{
void loginSuccess(UserInfoModel userInfoModel);
void loginFailed();
}
}
创建 Model
Model 层主要是对数据操作的,我们这里开启线程模拟登录操作:
/**
* @author SmartSean Created on 2018/1/25 16:49.
*/
public class Login1Model implements Login1Contract.ILoginModel {
@Inject
public Login1Model() {
}
@Override
public void login(final String username, final String password, final Login1Contract.OnLoginListener loginListener) {
new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if ("sean".equals(username) && "2".equals(password)) {
UserInfoModel userInfoModel = new UserInfoModel();
userInfoModel.setUsername(username);
userInfoModel.setPassword(password);
loginListener.loginSuccess(userInfoModel);
} else {
loginListener.loginFailed();
}
}
}.start();
}
@Override
public void onDestroy() {
// TODO: 2018/1/25 销毁持有的引用
}
}
创建 Presenter
Presenter 层主要是连接 View 层和 Model 层的桥梁:
public class Login1Presenter extends BasePresenter<Login1Contract.ILoginModel, Login1Contract.ILoginView> {
@Inject
public Login1Presenter(Login1Contract.ILoginModel model, Login1Contract.ILoginView view) {
super(model, view);
}
private Handler mHandler = new Handler();
/**
* 模拟登陆
*/
public void login() {
mView.showLoading();
mModel.login(mView.getUserName(), mView.getPassword(), new Login1Contract.OnLoginListener() {
@Override
public void loginSuccess(final UserInfoModel userInfoModel) {
mHandler.post(new Runnable() {
@Override
public void run() {
mView.toMainActivity(userInfoModel);
mView.hideLoading();
}
});
}
@Override
public void loginFailed() {
mHandler.post(new Runnable() {
@Override
public void run() {
mView.showFailedError();
mView.hideLoading();
}
});
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
// TODO: 2018/1/25 销毁持有的引用
}
/**
* 清除账户名和密码
*/
public void clear() {
mView.clearUserName();
mView.clearPassword();
}
}
创建Activity
public class Login1Activity extends BaseMvpActivity<Login1Presenter> implements Login1Contract.ILoginView {
private EditText usernameEt, passwordEt;
private Button loginBtn, clearBtn;
private ProgressDialog progressDialog;
private Context mContext;
private static final String TAG = "Login1Activity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login1);
initView();
mContext = this;
DaggerLogin1Component.builder().login1Module(new Login1Module(this)).build().inject(this);
}
private void initView() {
usernameEt = findViewById(R.id.username_et);
passwordEt = findViewById(R.id.password_et);
loginBtn = findViewById(R.id.login_btn);
clearBtn = findViewById(R.id.clear_btn);
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.login();
}
});
clearBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.clear();
}
});
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("加载中");
}
@Override
public String getUserName() {
return usernameEt.getText().toString().trim();
}
@Override
public String getPassword() {
return passwordEt.getText().toString().trim();
}
@Override
public void clearUserName() {
usernameEt.setText("");
}
@Override
public void clearPassword() {
passwordEt.setText("");
}
@Override
public void showLoading() {
progressDialog.show();
}
@Override
public void hideLoading() {
progressDialog.dismiss();
}
@Override
public void toMainActivity(UserInfoModel userInfoModel) {
MainActivity.startAction(this, userInfoModel.getUsername());
finish();
}
@Override
public void showFailedError() {
Log.d(TAG, "showFailedError: 登陆失败");
Toast.makeText(mContext, "登陆失败,请重新尝试", Toast.LENGTH_SHORT).show();
}
}
具体的代码 地址