Android MVP升級(jí)路(二)輕項(xiàng)目標(biāo)配之時(shí)尚版

Android MVP升級(jí)路系列文章目錄:

第一篇文章的結(jié)尾對(duì)時(shí)尚版MVP結(jié)構(gòu)做了一個(gè)簡(jiǎn)單的預(yù)告,下面繼續(xù)從時(shí)尚版MVP說(shuō)起。

時(shí)尚版MVP架構(gòu) - Model層的優(yōu)化

在從乞丐版MVP架構(gòu)優(yōu)化成平民版MVP架構(gòu)的過程中,幾乎每個(gè)單元都做了很大優(yōu)化并封裝到了base層,但是唯獨(dú)Model層沒什么變化。所以,時(shí)尚版MVP架構(gòu)的優(yōu)化主要就是對(duì)Model層的優(yōu)化。

單獨(dú)封裝,集中管理

Model層相比其他單元來(lái)說(shuō)比較特殊,因?yàn)樗鼈兏褚粋€(gè)整體,只是單純的幫上層拿數(shù)據(jù)而已。再就是MVP的理念是讓業(yè)務(wù)邏輯互相獨(dú)立,這就導(dǎo)致每個(gè)的網(wǎng)絡(luò)請(qǐng)求也被獨(dú)立成了單個(gè)Model,這種方式在實(shí)際開發(fā)中就會(huì)出現(xiàn)一些問題:

  • 無(wú)法對(duì)所有Model統(tǒng)一管理。
  • 每個(gè)Model對(duì)外提供的獲取數(shù)據(jù)方法不一樣,上層請(qǐng)求數(shù)據(jù)沒有規(guī)范。
  • 代碼冗余高,網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求除URL和參數(shù)外其他大概都一樣的。
  • 對(duì)已存在的Model管理困難,不能直觀的統(tǒng)計(jì)已存在的Model。

所以我們更希望Model層是一個(gè)龐大且獨(dú)立單一模塊,請(qǐng)求方式規(guī)范化,管理Model更加直觀。

時(shí)尚版MVP架構(gòu)Model層結(jié)構(gòu)

如上圖所示,時(shí)尚版MVP架構(gòu)的Model層中,Presenter 請(qǐng)求數(shù)據(jù)不再直接調(diào)用具體的Model對(duì)象,統(tǒng)一以 DataModel 類作為數(shù)據(jù)請(qǐng)求層的入口,以常量類 Token 區(qū)別具體請(qǐng)求。 DataModel會(huì)根據(jù)Token的不同拉取底層對(duì)應(yīng)的具體Model。

優(yōu)化之后的Model層是一個(gè)龐大而且獨(dú)立的模塊,對(duì)外提供統(tǒng)一的請(qǐng)求數(shù)據(jù)方法與請(qǐng)求規(guī)則,這樣做的好處有很多:

  • 數(shù)據(jù)請(qǐng)求單獨(dú)編寫,無(wú)需配合上層界面測(cè)試。
  • 統(tǒng)一管理,修改方便。
  • 利用Token類可以直觀的統(tǒng)計(jì)出已存在的請(qǐng)求接口。

代碼實(shí)現(xiàn)

根據(jù)上節(jié)結(jié)構(gòu)圖中的描述在考慮到實(shí)際情況,我們需要設(shè)計(jì)以下幾個(gè)類:

  • DataModel: 數(shù)據(jù)層頂級(jí)入口,項(xiàng)目中所有數(shù)據(jù)都由該類流入和流出,負(fù)責(zé)分發(fā)所有的請(qǐng)求數(shù)據(jù)。
  • Token:數(shù)據(jù)請(qǐng)求標(biāo)識(shí)類,定義了項(xiàng)目中所有的數(shù)據(jù)請(qǐng)求。
  • BaseModel:所有Model的頂級(jí)父類,負(fù)責(zé)對(duì)外提供數(shù)據(jù)請(qǐng)求標(biāo)準(zhǔn),對(duì)內(nèi)為所有Model提供請(qǐng)求的底層支持。

最后實(shí)現(xiàn)后理想的請(qǐng)求數(shù)據(jù)方法是:

BaseModel

BaseModel中定義了對(duì)外的請(qǐng)求數(shù)據(jù)規(guī)則,包括設(shè)置參數(shù)的方法和設(shè)置Callback的方法,還可以定義一些通用的數(shù)據(jù)請(qǐng)求方法,比如說(shuō)網(wǎng)絡(luò)請(qǐng)求的Get和Post方法。

public abstract class BaseModel<T>  {

    //數(shù)據(jù)請(qǐng)求參數(shù)
    protected String[] mParams;

    /**
     * 設(shè)置數(shù)據(jù)請(qǐng)求參數(shù)
     * @param args 參數(shù)數(shù)組
     */
    public  BaseModel params(String... args){
        mParams = args;
        return this;
    }

    // 添加Callback并執(zhí)行數(shù)據(jù)請(qǐng)求
    // 具體的數(shù)據(jù)請(qǐng)求由子類實(shí)現(xiàn)
    public abstract void execute(Callback<T> callback);

    // 執(zhí)行Get網(wǎng)絡(luò)請(qǐng)求,此類看需求由自己選擇寫與不寫
    protected void requestGetAPI(String url,Callback<T> callback){
        //這里寫具體的網(wǎng)絡(luò)請(qǐng)求
    }

    // 執(zhí)行Post網(wǎng)絡(luò)請(qǐng)求,此類看需求由自己選擇寫與不寫
    protected void requestPostAPI(String url, Map params,Callback<T> callback){
        //這里寫具體的網(wǎng)絡(luò)請(qǐng)求
    }

}

寫好了BaseModel后再看實(shí)現(xiàn)具體Model的方法:

public class UserDataModel extends BaseModel<String> {

    @Override
    public void execute(final Callback<String> callback) {
        
        // 模擬網(wǎng)絡(luò)請(qǐng)求耗時(shí)操作
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                // mParams 是從父類得到的請(qǐng)求參數(shù)
                switch (mParams[0]){
                    case "normal":
                        callback.onSuccess("根據(jù)參數(shù)"+mParams[0]+"的請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)成功");
                        break;

                    case "failure":
                        callback.onFailure("請(qǐng)求失敗:參數(shù)有誤");
                        break;

                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }
        },2000);
    }
}

從上面代碼段可以看出,實(shí)現(xiàn)具體的Model請(qǐng)求時(shí)必須要重寫B(tài)aseModel的抽象方法execute

DataModel

由于DataModel負(fù)責(zé)數(shù)據(jù)請(qǐng)求的分發(fā),所以最初打算作成一個(gè)簡(jiǎn)單工廠模式的樣子,通過switch(token)語(yǔ)句判斷要調(diào)用的Model。

但如果這樣設(shè)計(jì)的話,在實(shí)際開發(fā)中我們每次添加一個(gè)數(shù)據(jù)請(qǐng)求接口,不光需要新建對(duì)應(yīng)的Model和Token,還需要在DataModel類的switch(token)語(yǔ)句中新增加對(duì)應(yīng)的判斷,賊麻煩~

思來(lái)想去,我覺得利用反射機(jī)制會(huì)是一個(gè)比較理想的辦法,請(qǐng)求數(shù)據(jù)時(shí)以具體Model的包名+類型作為Token,利用反射機(jī)制直接找到對(duì)應(yīng)的Model。

public class DataModel {

    public static BaseModel request(String token){

        // 聲明一個(gè)空的BaseModel
        BaseModel model = null;

        try {
            //利用反射機(jī)制獲得對(duì)應(yīng)Model對(duì)象的引用
            model = (BaseModel)Class.forName(token).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return model;
    }

}

Token

由于上節(jié)中DataModel使用反射機(jī)制獲取對(duì)應(yīng)Model的引用,所以Token中存的就應(yīng)該是對(duì)應(yīng)Model的包名+類名:

public class Token {

    // 包名
    private static final String PACKAGE_NAME = "com.jesse.mvp.data.model.";

    // 具體Model
    public static final String API_USER_DATA = PACKAGE_NAME + "UserDataModel";
    

}

使用方式

完成了Model層之后再去Presenter調(diào)用數(shù)據(jù)時(shí)的樣子就舒服多了:

 DataModel
    // 設(shè)置請(qǐng)求標(biāo)識(shí)token
    .request(Token.API_USER_DATA)
    // 添加請(qǐng)求參數(shù),沒有則不添加
    .params(userId)
    // 注冊(cè)監(jiān)聽回調(diào)
    .execute(new Callback<String>() {

           @Override
           public void onSuccess(String data) {
               //調(diào)用view接口顯示數(shù)據(jù)
               mView.showData(data);
           }

           @Override
           public void onFailure(String msg) {
               //調(diào)用view接口提示失敗信息
               mView.showFailureMessage(msg);
           }

           @Override
           public void onError() {
               //調(diào)用view接口提示請(qǐng)求異常
               mView.showErrorMessage();
           }

           @Override
           public void onComplete() {
               // 隱藏正在加載進(jìn)度條
               mView.hideLoading();
           }
 });

添加Model的步驟

  1. 新建一個(gè)Model并繼承BaseModel,完成具體的數(shù)據(jù)請(qǐng)求。
  2. 在Token中添加對(duì)用的Model包名+類名。注意寫好注釋,方便以后查閱。

總結(jié)

經(jīng)過優(yōu)化的Model層很好的統(tǒng)一化了請(qǐng)求方法規(guī)范,利用BaseModel不僅有效的減少了數(shù)據(jù)請(qǐng)求的冗余代碼,最關(guān)鍵的還是得到了將所有Model的集中控制權(quán),例如我們想給所有的請(qǐng)求都加上coockies,直接在BaseModel層做處理即可。

時(shí)尚版MVP雖然只對(duì)Model層進(jìn)行了優(yōu)化,實(shí)際開發(fā)中已經(jīng)能發(fā)揮很大的作用。

下面一章旗艦版將三層同時(shí)優(yōu)化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容