Android MVP(七)静态代理模式的运用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/smile_Running/article/details/95339274

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页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<BasePresenter> mInjectPresenters;

    protected abstract void initLayout(@Nullable Bundle savedInstanceState);

    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);

        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<BasePresenter> mInjectPresenters;

    private View mLayoutView;

    protected abstract @LayoutRes int setLayout();

    protected abstract void initViews(@Nullable Bundle savedInstanceState);

    protected abstract void initData();

    @SuppressWarnings("ConstantConditions")
    protected <T extends View> 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<BasePresenter> 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<V extends IBaseView> 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<V extends IBaseView> 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 extends View> 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 上,喜欢的可以点个赞,如果有错的,欢迎纠错。

猜你喜欢

转载自blog.csdn.net/smile_Running/article/details/95339274