Android MVP(五)多个 Presenter 依赖注入

博主声明:

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

本文首发于此   博主威威喵  |  博客主页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<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 的问题情况,那么这个框架也变得越来越具有封装性,代码也更加的难以理解了。哈哈,这里给初学者是比较难以理解,如果我们能够掌握这些代码,最好是掌握这种思想,才能更上一层楼。这篇已经是第五篇连续的文章了,能写到这,说明我还是挺有毅力的,哈哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章