1. 程式人生 > >基於retrofit的網路框架的終極封裝(一):第一層(引數組裝層)的API設計

基於retrofit的網路框架的終極封裝(一):第一層(引數組裝層)的API設計

什麼是分層

app的架構,不管是MVC,MVP,MVVM,架構演變中,貫穿始終的概念都是分層和解耦.那麼這個分層和解耦怎麼體現出來?
簡單地說就是,我這一層接收上一層的輸入,上一層的你別管我怎麼處理,我最終會給你一個輸出/返回值,你完全不用理會我是怎麼處理的,只要有輸入,就會有輸出,而且一般是通過一個簡單的方法的呼叫來實現.

那麼,對於app中常用的網路層來說,怎麼樣的封裝才是最合理的?

我們的口號是:一行程式碼完成網路請求!

其實非常簡單,看介面文件你就知道了怎麼做了:就傳那些個引數,返回就是那個json之類的.把介面文件轉化為我們呼叫的一個方法,這網路層與上層的介面就可以說是設計好了.

最常見的字元流請求:

介面文件寫了什麼:(百度圖片隨便找的一個)

所以,我們網路層應該封裝成這樣一個方法:

傳入:

請求方式,url路徑,請求的引數key和值
注: 對於多個POST請求引數key-value,有的介面會定義成Map形式,有的會定義成Json形式.其實本質上都是一樣的,都是在requestBody上POST一個字串(字元流)出去,只不過前者的形式是a=4&t=5的形式,而後者是一個json形式.
總體上來講,還是前者比較多見,畢竟引數拼接形式與GET請求一致,伺服器端處理比較方便.

輸出/返回:

對於返回字元流資料的情況,可以歸納為返回一個String物件,怎麼處理就看具體情況了.

app中常見的是返回json格式的字串.當然,一般app裡需要的是解析好的javabean,那麼網路框架介面應該能直接返回一個解析好的javabean.

當然,更進一步來看,大多數規範的api(看看各大api市場,比如聚合api之類的)返回的json格式都具備以下特點:

一般為三個欄位,分別表示狀態碼,相關提示資訊,以及一個用於攜帶資料的欄位.(比如:{“code”:0,”msg”:”登入成功”,”data”:{…}}).攜帶資料的欄位可以攜帶任何型別的資料,null,數字,字串,jsonObject,JsonArray都可以.
在這裡,我把這種三個欄位的json稱為”標準格式的json”

封裝好的方法:

getString(String url, Map map, MyNetListener listener)//get請求,返回一個string,拿到這個string
postString( String url,  Map map,  MyNetListener listener)

getCommonJson(String url, Map map, Class clazz, MyNetListener listener)//get請求,返回一個json,將整個json解析成javabean
postCommonJson(String url, Map map, Class clazz, MyNetListener listener)

getStandardJson(String url, Map map, Class clazz, MyNetListener listener)//get請求,返回一個標準格式的json
postStandardJson(String url, Map map, Class clazz, MyNetListener listener) //post請求,返回一個標準格式的json,通過傳入的clazz,直接解析返回data欄位的javabean

//如果引數以json形式發出,那麼再呼叫方法setParamsAsJson()即可.

上傳和下載

下載:

一般是GET請求,傳入url.請求引數可有可無.最終下載得到一個檔案(路徑可預先指定),並且在下載的過程中有進度的回撥.
注意: 我上面一層不需要知道你的什麼網路流啊,斷點下載啊,分片多執行緒下載之類的細節.

download(String url, String savedpath, MyNetListener callback)

上傳

POST請求,傳入url,請求引數,檔案相關的key名,檔案的地址,最終返回一個結果(成功或失敗),並且在上傳的過程中有進度回撥.
同樣的,上一層不需要知道里面具體的header設定啊,斷點續傳啊,分片上傳之類的細節.
注意: http是支援多檔案上傳的.

upLoad(String url, Map<String,String> params,Map<String,String> files, MyNetListener callback)

你說還有很常見的拉取網路圖片並顯示在imageview中?
那就是圖片載入框架的事兒了,不應該交給網路框架處理.推薦fresco,以及我的這個FrescoUtils.

以上api的思維導圖概覽

基本api.jpg

返回的資料:統一的回撥

回撥採用抽象類的形式,而不是介面的形式

public void onPreExecute() {}//統一的開始
public void onFinish(){}//統一的結束

public  void onEmpty(){}//內容為空時--一般用在返回空的jsonArray時([]).


//成功的幾種情況:
public abstract void onSuccess(T response,String resonseStr);//主回撥

public  void onSuccessArr(List<T> response,String resonseStr){}//主回撥,需要時複寫

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);
}

//失敗的回撥

public void onError(String msgCanShow) {}//主回撥

 public void onUnFound() {
    onError("沒有找到該內容");
}
public    void onUnlogin(){
    onError("您還沒有登入");
}


 public void onCodeError(String msgCanShow,String hiddenMsg,int code) {
    if (TextUtils.isEmpty(msgCanShow)){
        onError("錯誤碼為:"+code);
    }else {
        onError(msgCanShow);
    }
}

//進度回撥
public void onProgressChange(long fileSize, long downloadedSize) {}

自定義請求時的一些引數

沒什麼好說的,直接看圖,清晰明瞭

自定義設定的api.jpg

一個請求示例

            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();

程式碼

附圖:整體層次圖

ps.是的,我沒有用逼格比較高的Rxjava,主要是因為比較習慣回撥模式,這種模式下,程式碼的聚合度比較高.