什么是分層
app的架構(gòu),不管是MVC,MVP,MVVM,架構(gòu)演變中,貫穿始終的概念都是分層和解耦.那么這個(gè)分層和解耦怎么體現(xiàn)出來(lái)?
簡(jiǎn)單地說(shuō)就是,我這一層接收上一層的輸入,上一層的你別管我怎么處理,我最終會(huì)給你一個(gè)輸出/返回值,你完全不用理會(huì)我是怎么處理的,只要有輸入,就會(huì)有輸出,而且一般是通過(guò)一個(gè)簡(jiǎn)單的方法的調(diào)用來(lái)實(shí)現(xiàn).
那么,對(duì)于app中常用的網(wǎng)絡(luò)層來(lái)說(shuō),怎么樣的封裝才是最合理的?
我們的口號(hào)是:一行代碼完成網(wǎng)絡(luò)請(qǐng)求!
其實(shí)非常簡(jiǎn)單,看接口文檔你就知道了怎么做了:就傳那些個(gè)參數(shù),返回就是那個(gè)json之類的.把接口文檔轉(zhuǎn)化為我們調(diào)用的一個(gè)方法,這網(wǎng)絡(luò)層與上層的接口就可以說(shuō)是設(shè)計(jì)好了.
最常見(jiàn)的字符流請(qǐng)求:
接口文檔寫了什么:(百度圖片隨便找的一個(gè))
所以,我們網(wǎng)絡(luò)層應(yīng)該封裝成這樣一個(gè)方法:
傳入:
請(qǐng)求方式,url路徑,請(qǐng)求的參數(shù)key和值
注: 對(duì)于多個(gè)POST請(qǐng)求參數(shù)key-value,有的接口會(huì)定義成Map形式,有的會(huì)定義成Json形式.其實(shí)本質(zhì)上都是一樣的,都是在requestBody上POST一個(gè)字符串(字符流)出去,只不過(guò)前者的形式是a=4&t=5的形式,而后者是一個(gè)json形式.
總體上來(lái)講,還是前者比較多見(jiàn),畢竟參數(shù)拼接形式與GET請(qǐng)求一致,服務(wù)器端處理比較方便.
輸出/返回:
對(duì)于返回字符流數(shù)據(jù)的情況,可以歸納為返回一個(gè)String對(duì)象,怎么處理就看具體情況了.
app中常見(jiàn)的是返回json格式的字符串.當(dāng)然,一般app里需要的是解析好的javabean,那么網(wǎng)絡(luò)框架接口應(yīng)該能直接返回一個(gè)解析好的javabean.
當(dāng)然,更進(jìn)一步來(lái)看,大多數(shù)規(guī)范的api(看看各大api市場(chǎng),比如聚合api之類的)返回的json格式都具備以下特點(diǎn):
一般為三個(gè)字段,分別表示狀態(tài)碼,相關(guān)提示信息,以及一個(gè)用于攜帶數(shù)據(jù)的字段.(比如:{"code":0,"msg":"登錄成功","data":{...}}).攜帶數(shù)據(jù)的字段可以攜帶任何類型的數(shù)據(jù),null,數(shù)字,字符串,jsonObject,JsonArray都可以.
在這里,我把這種三個(gè)字段的json稱為"標(biāo)準(zhǔn)格式的json"
封裝好的方法:
getString(String url, Map map, MyNetListener listener)//get請(qǐng)求,返回一個(gè)string,拿到這個(gè)string
postString( String url, Map map, MyNetListener listener)
getCommonJson(String url, Map map, Class clazz, MyNetListener listener)//get請(qǐng)求,返回一個(gè)json,將整個(gè)json解析成javabean
postCommonJson(String url, Map map, Class clazz, MyNetListener listener)
getStandardJson(String url, Map map, Class clazz, MyNetListener listener)//get請(qǐng)求,返回一個(gè)標(biāo)準(zhǔn)格式的json
postStandardJson(String url, Map map, Class clazz, MyNetListener listener) //post請(qǐng)求,返回一個(gè)標(biāo)準(zhǔn)格式的json,通過(guò)傳入的clazz,直接解析返回data字段的javabean
//如果參數(shù)以json形式發(fā)出,那么再調(diào)用方法setParamsAsJson()即可.
上傳和下載
下載:
一般是GET請(qǐng)求,傳入url.請(qǐng)求參數(shù)可有可無(wú).最終下載得到一個(gè)文件(路徑可預(yù)先指定),并且在下載的過(guò)程中有進(jìn)度的回調(diào).
注意: 我上面一層不需要知道你的什么網(wǎng)絡(luò)流啊,斷點(diǎn)下載啊,分片多線程下載之類的細(xì)節(jié).
download(String url, String savedpath, MyNetListener callback)
上傳
POST請(qǐng)求,傳入url,請(qǐng)求參數(shù),文件相關(guān)的key名,文件的地址,最終返回一個(gè)結(jié)果(成功或失敗),并且在上傳的過(guò)程中有進(jìn)度回調(diào).
同樣的,上一層不需要知道里面具體的header設(shè)置啊,斷點(diǎn)續(xù)傳啊,分片上傳之類的細(xì)節(jié).
注意: http是支持多文件上傳的.
upLoad(String url, Map<String,String> params,Map<String,String> files, MyNetListener callback)
你說(shuō)還有很常見(jiàn)的拉取網(wǎng)絡(luò)圖片并顯示在imageview中?
那就是圖片加載框架的事兒了,不應(yīng)該交給網(wǎng)絡(luò)框架處理.推薦fresco,以及我的這個(gè)FrescoUtils.
以上api的思維導(dǎo)圖概覽
返回的數(shù)據(jù):統(tǒng)一的回調(diào)
回調(diào)采用抽象類的形式,而不是接口的形式
public void onPreExecute() {}//統(tǒng)一的開始
public void onFinish(){}//統(tǒng)一的結(jié)束
public void onEmpty(){}//內(nèi)容為空時(shí)--一般用在返回空的jsonArray時(shí)([]).
//成功的幾種情況:
public abstract void onSuccess(T response,String resonseStr);//主回調(diào)
public void onSuccessArr(List<T> response,String resonseStr){}//主回調(diào),需要時(shí)復(fù)寫
public void onSuccessObj(T response,String responseStr,String data,int code,String msg){
onSuccess(response,responseStr);
}
public void onSuccessArr(List<T> response, String responseStr, String data, int code, String msg){
onSuccessArr(response,responseStr);
}
//失敗的回調(diào)
public void onError(String msgCanShow) {}//主回調(diào)
public void onUnFound() {
onError("沒(méi)有找到該內(nèi)容");
}
public void onUnlogin(){
onError("您還沒(méi)有登錄");
}
public void onCodeError(String msgCanShow,String hiddenMsg,int code) {
if (TextUtils.isEmpty(msgCanShow)){
onError("錯(cuò)誤碼為:"+code);
}else {
onError(msgCanShow);
}
}
//進(jìn)度回調(diào)
public void onProgressChange(long fileSize, long downloadedSize) {}
自定義請(qǐng)求時(shí)的一些參數(shù)
沒(méi)什么好說(shuō)的,直接看圖,清晰明了
一個(gè)請(qǐng)求示例
Map map8 = new HashMap<>();
map8.put("versionName","1.0.0");
map8.put("appType",0);
MyNetApi.postStandardJson("http://app.xxtt.com:9090/app/appVersion/getLatestVersion",
map8, VersionInfo.class, new MyNetListener<VersionInfo>() {
@Override
public void onSuccess(VersionInfo response, String resonseStr) {
Logger.e(resonseStr);
}
@Override
public void onError(String msgCanShow) {
super.onError(msgCanShow);
Logger.e(msgCanShow);
}
})
.setParamsAsJson()
.setIsAppendToken(false)
.setCustomCodeValue(1,2,3)
.start();
代碼
https://github.com/hss01248/NetWrapper
附圖:整體層次圖
![](https://dn-mhke0kuv.qbox.me/68c1289a93d8d2eddcef.jpg)
ps.是的,我沒(méi)有用逼格比較高的Rxjava,主要是因?yàn)楸容^習(xí)慣回調(diào)模式,這種模式下,代碼的聚合度比較高.