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 的問題情況,那麼這個框架也變得越來越具有封裝性,代碼也更加的難以理解了。哈哈,這裏給初學者是比較難以理解,如果我們能夠掌握這些代碼,最好是掌握這種思想,才能更上一層樓。這篇已經是第五篇連續的文章了,能寫到這,說明我還是挺有毅力的,哈哈。

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