博主声明:
转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。
本文首发于此 博主:威威喵 | 博客主页: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
连续写了好几天的 MVP 架构系列的文章,从一开始的 MVP 架构的介绍,到现在已经将 BaseMVP 框架的封装基本完成了,这段过程差不多花了一周的时间,对于我而言,无论在知识方面和写代码的能力方面,都有很大的提升。写博客,其实也是一个学习的过程,把自己学的总结一遍,我相信大部分人都像我一样,一个代码写完了,基本需求也搞定了,然后也就将代码扔到了一边,觉得当时会了的,可是,下次再遇到问题,发现又没有思路,没有方法去解决问题。
当然,另一方面,写博客可以发现自己的欠缺知识。比如,这阶段写的 BaseMVP 框架,用到的最多的就是反射的知识,我之前也写过一篇反射的文章,刚好直接开自己的博客回忆一下。所谓:温故而知新,可以为师矣。
那么,这篇文章就是给我们的 BaseMVP 框架做一个最后的总结与整改,这已经是第七篇连续的文章了,写着写着,都快可以形成短篇小说了,再不接近尾声的话,也没啥东西好写了。这几篇是紧密关联的,估计能看完的也比较少,大家都是忙于解决实际的问题,可能就我这总比较闲的大学生才会在这里长篇大论吧,但站在分享者的角度,我还是希望我的文章能有人看,能给大家带来帮助。
开篇写一点感想与总结做为铺垫,我比较喜欢这样做,如果喜欢看就纯粹当看看我的废话,不喜欢的就直接看问题和代码。我们基于上篇(Android MVP 架构(六)MVP 之 BaseFragment 的封装)封装了 BaseFragment 基类,但此时又出现了代码冗余的情况,上篇末尾我就抛出了这个问题,那么在这篇中,我们究竟如何解决这个重复的代码情况,先来看看重复部分的代码:
BaseActivity 基类代码:
public abstract class BaseActivity extends AppCompatActivity implements IBaseView {
/**
* 保存使用注解的 Presenter ,用于解绑
*/
private List mInjectPresenters;
protected abstract void initLayout(@Nullable Bundle savedInstanceState);
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);
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();
/**
* 解绑,避免内存泄漏
*/
for (BasePresenter presenter : mInjectPresenters) {
presenter.detach();
}
mInjectPresenters.clear();
mInjectPresenters = null;
}
@Override
public Context getContext() {
return this;
}
}
BaseFragment 基类代码:
public abstract class BaseFragment extends Fragment implements IBaseView {
private List mInjectPresenters;
private View mLayoutView;
protected abstract @LayoutRes int setLayout();
protected abstract void initViews(@Nullable Bundle savedInstanceState);
protected abstract void initData();
@SuppressWarnings("ConstantConditions")
protected T $(@IdRes int viewId) {
return this.getView().findViewById(viewId);
}
@SuppressWarnings({"unchecked", "TryWithIdenticalCatches"})
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(setLayout(), container, false);
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 (java.lang.InstantiationException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
throw new RuntimeException("SubClass must extends Class:BasePresenter");
}
}
}
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initViews(savedInstanceState);
initData();
}
@Override
public void onDestroy() {
super.onDestroy();
for (BasePresenter presenter : mInjectPresenters) {
presenter.detach();
}
mInjectPresenters.clear();
mInjectPresenters = null;
}
}
既然是做为框架而言,我们就应该去解决这个代码重复的问题。要想写好一个框架,这是我们必须要克服的一个问题,对比别人写的框架,多看看他们是如何解决重复性代码问题的方法,借鉴这些方法,引进我们的代码中,这就变成我们自己的东西了。对比 BaseActivity 和 BaseFragment 的代码,发现重复的代码如下:
重复处1:
重复处2:
对于重复的代码,我们一种最基本的是做法是抽取到父类中去,然后让这两个子类分别去继承它,这样可以做到代码的复用。但是,这里的抽取到父类的方法,其实并不合适在我们这个问题中去使用,因为 Java 是单继承的特性。
因为,我们的 BaseActivity 必须继承 Activity 才能启动,而 BaseFragment 又必须继承 Fragment 。所以这里不能瞎搞,无法用继承去搞的话,只能用接口的方法去试一试了,因为接口可以有多个。思来想去,这要这么搞呢,最终寻得良计,在这里用代理模式来处理这种情况,代理模式的代理方法必须是一个接口提供的,代码如下:
代理接口:IProxy 接口
package com.test.mvp.mvpdemo.mvp.v7.proxy;
public interface IProxy {
void bindPresenter();
void unbindPresenter();
}
代理接口中,提供了我们去绑定和解绑 Presenter 的抽象方法,具体的也就是上面重复的部分,我们需要新建一个接口实现类,用来统一代理重复的代码,代码如下:
IProxy 接口实现类: ProxyImpl 类
package com.test.mvp.mvpdemo.mvp.v7.proxy;
import com.test.mvp.mvpdemo.mvp.v7.basemvp.BasePresenter;
import com.test.mvp.mvpdemo.mvp.v7.basemvp.IBaseView;
import com.test.mvp.mvpdemo.mvp.v7.inject.InjectPresenter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class ProxyImpl implements IProxy {
private IBaseView mView;
private List mInjectPresenters;
public ProxyImpl(IBaseView view) {
this.mView = view;
mInjectPresenters = new ArrayList<>();
}
@SuppressWarnings({"unchecked", "TryWithIdenticalCatches"})
@Override
public void bindPresenter() {
//获得已经申明的变量,包括私有的
Field[] fields = mView.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(mView);
field.setAccessible(true);
field.set(mView, 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");
}
}
}
}
@Override
public void unbindPresenter() {
/**
* 解绑,避免内存泄漏
*/
for (BasePresenter presenter : mInjectPresenters) {
presenter.detach();
}
mInjectPresenters.clear();
mInjectPresenters = null;
}
}
把重复的代码,全部抽取到 ProxyImpl 类中去处理,这样我们在 BaseActivity 和 BaseFragment 中就不用去写重复的代码了,不过在此,需要再新建两个代理的实现类,一个是 ProxyActivity 类,专门用来代理与 Activity 有关的代码,另一个就是 ProxyFragment 类,专门用来代理与 Fragment 有关的代码,代码如下:
新建 ProxyActivity 类:
package com.test.mvp.mvpdemo.mvp.v7.proxy;
import com.test.mvp.mvpdemo.mvp.v7.basemvp.IBaseView;
public class ProxyActivity extends ProxyImpl {
public ProxyActivity(V view) {
super(view);
}
}
新建 ProxyFragment 类:
package com.test.mvp.mvpdemo.mvp.v7.proxy;
import com.test.mvp.mvpdemo.mvp.v7.basemvp.IBaseView;
public class ProxyFragment extends ProxyImpl {
public ProxyFragment(V view) {
super(view);
}
}
这里两个代理类暂时没上面代码,因为还没上面业务逻辑要处理。不过,必须要传入一个泛型的 IBaseView 对象,这里的原因就是我们的 ProxyImpl 类中的 presenter 调用 attach() 方法去绑定 View 时,这个 View 是继承 IBaseView 的,所以这必须要一个参数给它,通过继承 ProxyImpl 类将这个 view 用构造函数的方式传给父类。
到这里,就好了。总得来说,这里就新建了几个代理类,我们来看一下项目包发生的变化吧:
好了,你一定期待我们的 BaseActivity 和 BaseFragment 中的代码到底少了多少,或者你肯定想知道如何调用代理类,下面来看看吧:
修改 BaseActivity 基类:
public abstract class BaseActivity extends AppCompatActivity implements IBaseView {
private ProxyActivity mProxyActivity;
protected abstract void initLayout(@Nullable Bundle savedInstanceState);
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);
mProxyActivity = createProxyActivity();
mProxyActivity.bindPresenter();
initViews();
initData();
}
@SuppressWarnings("unchecked")
private ProxyActivity createProxyActivity() {
if (mProxyActivity == null) {
return new ProxyActivity(this);
}
return mProxyActivity;
}
@Override
protected void onDestroy() {
super.onDestroy();
mProxyActivity.unbindPresenter();
}
@Override
public Context getContext() {
return this;
}
}
调用很简单,首先实例化 ProxyActivity 对象,然后调用它父类的 bind 和 unbind 方法就可以了。很明显,把那一坨长长的代码抽掉了以后,BaseActivity 显得异常清爽,瞬间瘦身成功。这里我就不对 BaseFragment 进行说明了,代码步骤都一样。
做一下最后的总结,这里运用了一个设计模式:代理模式,不懂的可以去查一查。这篇文章的结束,也代表这本系列文章的结束,因为我们 BaseMVP 框架已经可以上手使用了,我们从第一篇的 MVP 的使用到这篇的 BaseMVP 封装过程中发现的一些问题和解决方法都差不多仔仔细细的写清楚,写明白了。当然,最重要的不是代码,而是解决问题的思路和方法。
如果你有足够的学习热情,你可以从第一篇到最后一篇依次看下来,我相信你收获的一定不少。对了,这里肯定有人需要源代码,我会发到 csdn 和 github 上,喜欢的可以点个赞,如果有错的,欢迎纠错。