Android MVP(四)運用反射配置泛型 Model

博主聲明:

轉載請在開頭附加本文鏈接及作者信息,並標記爲轉載。本文由博主 威威喵 原創,請多支持與指教。

本文首發於此   博主威威喵  |  博客主頁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 內存泄漏分析與動態代理)的 BaseMVP 框架基礎上進行配置和修改的,上篇我們介紹了 MVP 可能存在的內存泄漏的問題,還有就是如何使用動態代理,我們把同一段代碼或同一個業務邏輯判斷操作項抽離出來,用的就是 AOP 思想。AOP 思想的一種編程手段,使用動態代理的方式進行抽離重複的代碼或進行統一的邏輯處理。

    使用動態代理對 View 層進行抽離統一的邏輯判斷,這一項工作我們在上篇文章中已經完成了。緊接着,我們就應該考慮其他層的封裝處理。這篇文章,我們就拿 Model 層開刀。

    首先,看看我們的 BaseMVP 框架中的 Model 層,Model 雖然提供數據源都各不相同,但每次引用它時,都需要在 Presenter 層拿到它的引用纔行,最簡單的引用方式是 new 出它的實例,比如下面的 MianPresenter 中的代碼:

/**
 * presenter 層,承擔業務邏輯處理,數據源處理等
 */
public class MainPresenter extends BasePresenter<MainContract.IMainView> implements MainContract.IMainPresenter {

    private MainContract.IMainModel mModel;

    @Override
    public void attech(IBaseView view) {
        super.attech(view);
        mModel = new DataModel();
    }

    @Override
    public void handlerData() {
        getView().showDialog();

        mModel.requestBaidu(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String content = response.body().string();
                getView().succes(content);
            }
        });
    }

    @Override
    public void detech() {
        super.detech();
        /**
         * 釋放內存、關閉網絡請求、關閉線程等操作
         */
        Log.d("==========", "detech: 解除綁定,釋放內存");
    }
}

    就像代碼中的 IMainModel 接口,我們每次要獲取數據源時,都要去 new 它的一個實現類,這種方法都是可以的。但是,每次去 new 又顯得麻煩,而且通常來說,一個 Presenter 和一個 Model 是一對一的關係,所以,我們想到了創建對象的另一種方式:通過反射來獲取。

    首先,分析一下每一個 Model 肯定是不同的類型,這裏就必須用到泛型。又因爲 Model 層只有與 Presenter 層纔有引用的關係,Presenter 持有 Model 的引用,所以這裏的 Presenter 所持有的必定是一個泛型的 Model ,而不是具體的。而且我們是寫 BaseMVP 框架,所以泛型應該封裝到 BasePresenter 基類中去,才能讓實現它的子類去調用。

    於是呢,我們的第一步,創建 BaseModel 基類,這裏沒有什麼新的方法,主要用於泛型與繼承的關係。如果有什麼比較特殊的數據源或通用的可以在基類中提供。

    第4個版本我們與前3個版本對比,就添加了一個 BaseModel 類,來看看包詳情

創建 BaseModel 基類:

package com.test.mvp.mvpdemo.mvp.v4.basemvp;

public abstract class BaseModel {
}

    第二步,我們的 DataModel 就要繼承 BaseModel 基類,提供相應的 Presenter 提供特有的數據。

修改 DataModel 實現類:

package com.test.mvp.mvpdemo.mvp.v4.model;

import com.test.mvp.mvpdemo.mvp.v4.MainContract;
import com.test.mvp.mvpdemo.mvp.v4.basemvp.BaseModel;

import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;

/**
 * model 層,請求網絡或數據庫,提供數據源(原始數據)
 */
public class DataModel extends BaseModel implements MainContract.IMainModel {

    @Override
    public void requestBaidu(Callback callback) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://www.baidu.com/")
                .build();
        client.newCall(request).enqueue(callback);
    }
}

    接下來,就是我們的重點:BasePresenter 類的修改與處理,我們要爲 BasePresenter 類再添加一個泛型參數,提供對不同 Model 實現類的支持。再者,就是使用反射來實例化 Model 對象,這裏可能是比較容易出錯的。一個是對反射的理解,另一個是對泛型的理解。我們先看看代碼,再進行解釋。

修改 BasePresenter 基類:

package com.test.mvp.mvpdemo.mvp.v4.basemvp;

import android.util.Log;

import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

public abstract class BasePresenter<V extends IBaseView, M extends BaseModel> implements IBasePresenter {
    private SoftReference<IBaseView> mReferenceView;
    private V mProxyView;
    private M mModel;

    @SuppressWarnings({"unchecked", "TryWithIdenticalCatches"})
    @Override
    public void attach(IBaseView view) {
        //使用軟引用創建對象
        mReferenceView = new SoftReference<>(view);
        //使用動態代理做統一的邏輯判斷 aop 思想
        mProxyView = (V) Proxy.newProxyInstance(view.getClass().getClassLoader(), view.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                if (mReferenceView == null || mReferenceView.get() == null) {
                    return null;
                }
                return method.invoke(mReferenceView.get(), objects);
            }
        });

        //通過獲得泛型類的父類,拿到泛型的接口類實例,通過反射來實例化 model
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        if (type != null) {
            Type[] types = type.getActualTypeArguments();
            try {
                mModel = (M) ((Class<?>) types[1]).newInstance();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }

    }

    @SuppressWarnings("unchecked")
    public V getView() {
        return mProxyView;
    }

    protected M getModel() {
        return mModel;
    }

    @Override
    public void detach() {
        mReferenceView.clear();
        mReferenceView = null;
    }
}

    先看類的參數,我們添加了一個繼承自剛剛寫的 BaseModel 類的泛型參數,這裏不難理解。可能比較難理解的是如何實例化 Model 對象吧,也就是這個例子中的 DataModel 類的實例化。

    既然這樣,我們先來看看在 MainPresenter 實現類中該如何傳遞這個泛型 Model ,應該是傳入 DataModel 吧,來看看我們的MainPresenter 類代碼:
修改 MainPresenter  實現類:

/**
 * presenter 層,承擔業務邏輯處理,數據源處理等
 */
public class MainPresenter extends BasePresenter<MainContract.IMainView, DataModel> implements MainContract.IMainPresenter {

    @Override
    public void handlerData() {
        getView().showDialog();

        getModel().requestBaidu(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String content = response.body().string();
                getView().succes(content);
            }
        });
    }

    @Override
    public void detach() {
        super.detach();
        /**
         * 釋放內存、關閉網絡請求、關閉線程等操作
         */
        Log.d("==========", "detech: 解除綁定,釋放內存");
    }
}

    這裏我們傳入的是 DataModel 的實例,而 MainPresenter 類是繼承自 BasePresenter 基類的,所以在 BasePresenter 的反射代碼中的 this 指的就是 MainPresenter 類的對象。看如下反射代碼:

反射代碼部分:

        //通過獲得泛型類的父類,拿到泛型的接口類實例,通過反射來實例化 model
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();

        Log.d("===========", "attach: "+this.getClass().getSimpleName());
        if (type != null) {
            Type[] types = type.getActualTypeArguments();
            try {
                mModel = (M) ((Class<?>) types[1]).newInstance();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }

通過 Log 打印就能驗證一下,看看是否正確。

    我們拿到的是 MainPresenter 類的對象,這不是我們想要的啊,我們想要的是 DataModel 類的對象。又因爲這裏 DataModel 是通過 BasePresenter 類的泛型傳進去的,所以我們通過反射機制是可以獲取到 MainPresenter 的父類 BasePresenter 類。再接着,我們應該去獲取 BasePresenter 的泛型參數,這裏的 BasePresenter 有兩個泛型參數,它返回一個 Type[] 數組。而第二個就是我們需要的真正的 DataModel 類對象了,最後通過類對象的 newInstance() 實例化就可以了。

    看起來是有點難理解,不過還是 Java 的基礎啊,正是運用了 Java 的反射來動態的創建 Model 層,這是我們寫框架的必備知識。那麼,到這裏我們又解決了 Model 層需要手動 new 來實例化的問題,通過反射可以讓我們的代碼變得更加的優雅,看起來也比較舒服。當然,對於初學者來說,這樣的代碼看起來是有點難理解,還是要死磕到底啊。

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