Android MVP 多个 Presenter 依赖注入
基于上篇(Android MVP 架构 MVP 泛型 Model 的配置)我们讲诉的是一对一的 Presenter 和 Model 的关系,利用反射来实例化我们的 Model 层,从而解放了我们每次创建 Model 时都要去 new 的麻烦事儿。明显,代码的逼格更高了一个档次,但是还不够,我们这篇文章中要解决的是 View 与 Presener 层的一对多的关系,这个很正常,当业务相对比较多的时候,我们的 Presenter 层并不是只有唯一的一个,这种情况就是我们的一个 View 层对应多个 Presenter 的现象。
从我们的前几次版本封装的代码来看,我们在 BaseActivity 基类中是提供了一个抽象方法给它的子类调用,它的返回类型是一个泛型的 Presenter 实现类,而我们 Activity 继承 BaseActivity 的时候,都是直接 new 一个 Presenter 实例的,先来看看代码吧,这样更能够体现这个问题。
BaseActivity 基类,这里我只抽了部分代码
public abstract class BaseActivity<P extends IBasePresenter> extends AppCompatActivity implements IBaseView {
private P mPresenter;
protected abstract void initLayout(@Nullable Bundle savedInstanceState);
protected abstract P setPresenter();
protected abstract void initViews();
protected abstract void initData();
@SuppressWarnings("SameParameterValue")
protected <T extends View> T $(@IdRes int viewId) {
return findViewById(viewId);
}
看代码中的 setPresenter() 方法,子类必须传入一个泛型的实现类。所以,子类代码如下:
MainActivity 部分代码:
public class MainActivity extends BaseActivity<MainContract.IMainPresenter> implements MainContract.IMainView {
private TextView tv;
@Override
protected void initLayout(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
}
@Override
protected void initViews() {
tv = $(R.id.tv);
}
@Override
protected void initData() {
getPresenter().handlerData();
}
@Override
protected MainContract.IMainPresenter setPresenter() {
return new MainPresenter();
}
这里返回的是一个实现了 IMainPresenter 接口的实现类,这里为什么能够 new 实现类呢?其实就是 Java 三大特性中的多态。当然,重点不在这,而是我们这个 MainActivity 需要多个 Presenter 怎么办?
第一种,最简单的就是 new 一个你需要的不同的 Presenter 实现类,只要关联上 View 层,都可以使用。这种办法也是在没有其他方法的情况下,勉为其难的接受。
第二种,是使用 Dagger 框架进行依赖注入,那你又没学过怎么办?除了去学习,当然,我们还可以自己写一个依赖注入嘛,依赖注入的方式有很多种,比如:使用接口的方式、构造函数、方法等,还有就是我们的反射的方式。
所以,由于这里的 Presenter 是泛型的类,而不是具体实现类,我们也就只有考虑用反射来获取对象并实例化了。从上篇文章种的动态创建 Model 就是利用的反射的方式,既然学过了,这里应该也不难吧。来吧,直接上代码:
修改 BaseActivity 代码:
package com.test.mvp.mvpdemo.mvp.v5.basemvp;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import com.test.mvp.mvpdemo.mvp.v5.inject.InjectPresenter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseActivity<P extends IBasePresenter> extends AppCompatActivity implements IBaseView {
private P mPresenter;
/**
* 保存使用注解的 Presenter ,用于解绑
*/
private List<BasePresenter> mInjectPresenters;
protected abstract void initLayout(@Nullable Bundle savedInstanceState);
protected abstract P setPresenter();
protected abstract void initViews();
protected abstract void initData();
@SuppressWarnings("SameParameterValue")
protected <T extends View> T $(@IdRes int viewId) {
return findViewById(viewId);
}
@SuppressWarnings({"unchecked", "TryWithIdenticalCatches"})
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initLayout(savedInstanceState);
/**
* 实例化和绑定 P 层
*/
this.mPresenter = setPresenter();
if (mPresenter != null) {
this.mPresenter.attach(this);
}
mInjectPresenters = new ArrayList<>();
//获得已经申明的变量,包括私有的
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
//获取变量上面的注解类型
InjectPresenter injectPresenter = field.getAnnotation(InjectPresenter.class);
if (injectPresenter != null) {
try {
Class<? extends BasePresenter> type = (Class<? extends BasePresenter>) field.getType();
BasePresenter mInjectPresenter = type.newInstance();
mInjectPresenter.attach(this);
field.setAccessible(true);
field.set(this, mInjectPresenter);
mInjectPresenters.add(mInjectPresenter);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}catch (ClassCastException e){
e.printStackTrace();
throw new RuntimeException("SubClass must extends Class:BasePresenter");
}
}
}
initViews();
initData();
}
@Override
protected void onDestroy() {
super.onDestroy();
/**
* 解绑,避免内存泄漏
*/
this.mPresenter.detach();
this.mPresenter = null;
for (BasePresenter presenter : mInjectPresenters) {
presenter.detach();
}
mInjectPresenters.clear();
mInjectPresenters = null;
}
@Override
public Context getContext() {
return this;
}
public P getPresenter() {
return mPresenter;
}
}
因为 Presenter 的实例化是在 Activity 中进行的,所以我们得从 BaseActivity 中入手。我们只有一个目的:就是实例化不同的 Presenter 实现类。在泛型的类型面前,我们只能做的也就是通过反射来实例化。看看我们的反射部分关键代码:
//获得已经申明的变量,包括私有的
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
//获取变量上面的注解类型
InjectPresenter injectPresenter = field.getAnnotation(InjectPresenter.class);
if (injectPresenter != null) {
try {
Class<? extends BasePresenter> type = (Class<? extends BasePresenter>) field.getType();
BasePresenter mInjectPresenter = type.newInstance();
mInjectPresenter.attach(this);
field.setAccessible(true);
field.set(this, mInjectPresenter);
mInjectPresenters.add(mInjectPresenter);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}catch (ClassCastException e){
e.printStackTrace();
throw new RuntimeException("SubClass must extends Class:BasePresenter");
}
}
}
首先,这里的 this 指的就是继承了 BaseActivity 的子类,也就是我们的 MainActivity 类,我们得先获取到 MainActivity 中所有的成员变量,这里要注意的是,必须使用 getDeclaredFields() 方法进行获取,因为有可能某些成员变量会被定义为私有的。
我们先跳过这个注解,下面来说明。接着,我们在循环遍历被这个注解过的变量,然后进行一个类型的强制转换。通过 newInstance 实例话 BasePresenter 对象。因为,这里我们进行了实例化,所以在这必须要和我们的 View 关联起来,保证可以持有 View 的引用。最后的 set 方法,要将 this 和我们反射创建的实例化对象进行赋值。这里有必要解释一下,this 代表当前 MainActivity 的对象,它意味这去调用 setter() 方法将反射创建的 mInjectPresenter 对象赋值给 MainActivity 通过注解的 MainPresenter 对象,所以呢,这里很清楚的解释了,为什么一个注解符号就可以实例化对象的原因,其实它的背后已经做了大量你看不到的工作。
最后,我们去获取那些被 InjectPresenter 注解的变量,这里的 InjectPresenter 是我们自己定义的一个注解,它必须是一个接口类型,看代码:
新建 InjectPresenter 注解类:
package com.test.mvp.mvpdemo.mvp.v5.inject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectPresenter {
}
这里我们使用 @Target 指定它的注解类型为变量,指定它被保留在运行时期。这里的代码呢,没什么难度。然后,我们在 View 层需要引用的 Presenter 实现类变量头上加个 @InjectPresenter 注解就可以了。当然,你也可以添加多个不同 Presenter,这就解决了我们的 View 与 Presenter 一对多的问题,下面来看看代码吧:
修改 MainActivity 类:
package com.test.mvp.mvpdemo.mvp.v5.view;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.widget.TextView;
import android.widget.Toast;
import com.test.mvp.mvpdemo.R;
import com.test.mvp.mvpdemo.mvp.v5.MainContract;
import com.test.mvp.mvpdemo.mvp.v5.basemvp.BaseActivity;
import com.test.mvp.mvpdemo.mvp.v5.inject.InjectPresenter;
import com.test.mvp.mvpdemo.mvp.v5.presenter.MainPresenter;
/**
* MVP 的写法,Version 5: 依赖注入,解决多个 Presenter 的问题
*
* @author 神探丶威威猫
* @blog https://blog.csdn.net/smile_running
* @warning 点个赞哦,评个论哦
*/
public class MainActivity extends BaseActivity<MainContract.IMainPresenter> implements MainContract.IMainView {
private TextView tv;
@InjectPresenter
private MainPresenter mPresenter;
@Override
protected void initLayout(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
}
@Override
protected void initViews() {
tv = $(R.id.tv);
}
@Override
protected void initData() {
// getPresenter().handlerData();
mPresenter.handlerData();
}
@Override
protected MainContract.IMainPresenter setPresenter() {
return null;
}
@Override
public void showDialog() {
ProgressDialog dialog = new ProgressDialog(getContext());
dialog.show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dialog.dismiss();
}
}, 1500);
}
@Override
public void succes(String content) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(), "" + content, Toast.LENGTH_SHORT).show();
tv.setText(content);
}
});
}
}
从上面的代码中,我们的 MainPresenter 头上加了个 @InjectPresenter 注解,这就意味这它实例化成功了,我们可以尽情的去调它的方法。为了测试正确性,我们把 getPresenter() 给注释了,并且 setPresenter() 方法都没有返回值,说明我们当前用的就是注解的对象。运行一下,可以成功!
这里还存在一个问题放到最后来讲,因为我们在反射的时候要进行关联 View 层,所以在那个时候已经绑定了。既然已经绑定了,那必须要有解绑的操作,否则就会出现问题。因为,这里可能有多个 Presenter 要进行绑定,也有多个 Presenter 进行解绑。所以,就来一个集合存放不同的 Presenter,在反射时绑定并它添加到集合中去,在 View 的 onDestory() 方法中,摧毁视图时进行解绑,这样就不会出现问题了。代码非常简单,就是集合添加和遍历操作。代码已经在上面了,所以这里就不再贴出来了。
解决这多个 Presenter 的问题情况,那么这个框架也变得越来越具有封装性,代码也更加的难以理解了。
最后
最后我准备了一些面试的知识汇总,数据结构,计算机网络等等都有。自己整理和分类的,还请尊重知识产出。
分享给大家的资料包括高级架构技术进阶脑图、Android开发面试专题资料,还有高级进阶架构资料包括但不限于【高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术】希望能帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也是可以分享给身边好友一起学习的!
资料免费领取方式:私信回复“架构资料”即可获取