博主声明:
转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。
本文首发于此 博主:威威喵 | 博客主页:https://blog.csdn.net/smile_running
MVP 架构系列文章:
Android MVP 架构(一)MVP 架构介绍与实战运用
Android MVP 架构(二)MVP 之 BaseMVP 基础框架设计
Android MVP 架构(三)MVP 内存泄漏分析与动态代理
Android MVP 架构(四)MVP 泛型 Model 的配置
Android MVP 架构(五)MVP 多个 Presenter 依赖注入
Android MVP 架构(六)MVP 之 BaseFragment 的封装
Android MVP 架构(七)MVP 之代理模式消除重复代码(结束)
源码地址:
github下载:MVPDemo
csdn 下载:MVPDemo
基于上篇(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 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 $(@IdRes int viewId) {
return findViewById(viewId);
}
看代码中的 setPresenter() 方法,子类必须传入一个泛型的实现类。所以,子类代码如下:
MainActivity 部分代码:
public class MainActivity extends BaseActivity 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 extends AppCompatActivity implements IBaseView {
private P mPresenter;
/**
* 保存使用注解的 Presenter ,用于解绑
*/
private List mInjectPresenters;
protected abstract void initLayout(@Nullable Bundle savedInstanceState);
protected abstract P setPresenter();
protected abstract void initViews();
protected abstract void initData();
@SuppressWarnings("SameParameterValue")
protected 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 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 的问题情况,那么这个框架也变得越来越具有封装性,代码也更加的难以理解了。哈哈,这里给初学者是比较难以理解,如果我们能够掌握这些代码,最好是掌握这种思想,才能更上一层楼。这篇已经是第五篇连续的文章了,能写到这,说明我还是挺有毅力的,哈哈。