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層中,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的步驟
- 新建一個(gè)Model并繼承BaseModel,完成具體的數(shù)據(jù)請(qǐng)求。
- 在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)化。