1. 程式人生 > >Retrofit--合理封裝回調能讓你的專案高逼格

Retrofit--合理封裝回調能讓你的專案高逼格

本文已授權微信公眾號:鴻洋(hongyangAndroid)在微信公眾號平臺原創首發。

緒論

前面我們討論了使用Retrofit時怎樣去設定OKHttp,包括持久化管理Cookie設定網路超時設定列印攔截器設定快取Header等等,詳細可檢視
Retrofit–使用Retrofit時怎樣去設定OKHttp
Retrofit+OKHttp 教你怎麼持久化管理Cookie
今天我們討論的主題是怎麼封裝回調才能完美的適應自己的需求。我們都知道程式碼風格是每個人都有自己的風格,不可能完全一樣,那麼我們寫出來的程式碼怎樣能夠儘可能的去滿足需求呢?換句話說怎樣才能設計出高可用、高解耦、高可維護的程式碼架構呢?其實本人也是渣渣一個,看了一些別人的程式碼再加上自己的理解,今天把自己在用Retrofit時的一些想法分享給大家。

這裡寫圖片描述

封裝背景:

在開始用Retrofit的時候在網上一搜,搜出來好多教程,而且口碑很好,所以我打算新的專案由原來的Xutils框架轉戰Retrofit。

Retrofit和Java領域的ORM概念類似, ORM把結構化資料轉換為Java物件,而Retrofit 把REST API返回的資料轉化為Java物件方便操作。同時還封裝了網路程式碼的呼叫。

看了一些資料後大致瞭解到,Retrofit 2.0利用註解的形式將我們訪問伺服器的URL以及引數封裝成了java物件,而OKHttp依舊去執行網路請求。現在網上的教程一般都告訴我們了怎樣去使用Retrofit(隨意找了一個教程):
1.首先定義一個介面:

public interface APIService {
  @GET("/users/{user}/repos")
  List<Repo> listRepos(@Path("user") String user);
}

2.接著通過Retrofit.Builder()去建立這個url以及引數

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("xxx")
            .addConverterFactory(GsonConverterFactory.create())
            .build
(); APIService service = retrofit.create(APIService.class);

3.最後通過定義Call去執行網路請求

Call<User> call = apiService.getUser(username);
call.enqueue(new Callback<User>() {
    @Override
    public void onResponse(Response<User> response) {
        int statusCode = response.code();
        User user = response.body();  
    }
    @Override
    public void onFailure(Throwable t) {
        // Log error here since request failed
    }
});

然後我們就能在網路請求成功失敗的回調出去更新UI了。

自定義響應體CallModel

很簡單的實現了一個網路請求,當然我在剛開始的時候一直也都是這麼使用的,因為Retrofit自己封裝了JSON解析的過程,我們只需在建造Retrofit的時候加入addConverterFactory(GsonConverterFactory.create())就可以了,但是得保證我們定義的API介面的時候Call裡面的bean要和伺服器返回的介面一一對應,否則這個欄位就會為null,甚至網路請求失敗報錯json轉化異常。

我們都知道伺服器返回的結果一般都是下面這種格式的:

這裡寫圖片描述
一定是有code狀態碼和返回資訊的,我之前的用法和教程一樣,說將伺服器返回的結果複製下來直接在AS上面利用GsonFormat轉化就OK了,確實很方便,但是這樣下來每個bean裡面都會有重複的erroe_code和message或者伺服器返回其他,所以我是這樣做的:

public class BaseCallModel<T> {

    public int errno;
    public String msg;
    public T data;

}

定義一個BaseCallModel,利用泛型去適合伺服器返回的所有的bean,而你在定義一個API介面的時候就可以這樣定義:

@GET("user/login")
Call<BaseCallModel<User>> doLogin(@Query("email") String email, @Query("password") String pwd);

自定義CallBack

自定義完響應體之後,那麼問題又來了,error_code會有不同的值,而不同的值需要我們所做的操作不同,舉個例子,我們的需求是

  • 0-請求成功
  • 1-請求失敗,登入過期
  • 2-請求失敗,無許可權
  • 3-請求失敗-餘額不足
  • ….
    假如登入過期需要我們重新登入,跳轉到登入介面的話,我們不可能在每個網路請求的回撥裡面都去判斷error_code吧?那樣豈不是很….,所以就有了它:

import java.net.ConnectException;
import java.net.SocketTimeoutException;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Created by Hankkin on 2016/6/4.
 */
public abstract class MyCallback<T extends BaseCallModel> implements Callback<T> {
    @Override
    public void onResponse(Call<T> call, Response<T> response) {
        if (response.raw().code() == 200) {//200是伺服器有合理響應
            if(response.body().errno == 0){
                onSuc(response);
            }
            else if (response.body().errno == 1){

            }
            else if (response.body().errno == 2){
                onAutoLogin();
            }
            else if (){

            }
            .
            .
            .
            else {
                onFail(response.body().msg);
            }

        } else {//失敗響應
            onFailure(call, new RuntimeException("response error,detail = " + response.raw().toString()));
        }
    }

    @Override
    public void onFailure(Call<T> call, Throwable t) {//網路問題會走該回調
        if(t instanceof SocketTimeoutException){
            //
        }else if(t instanceof ConnectException){
          //
        }else if(t instanceof RuntimeException){
            //
        }
        onFail(t.getMessage());
    }

    public abstract void onSuc(Response<T> response);

    public abstract void onFail(String message);

    public abstract void onAutoLogin();

}



我自定義了一個抽象類實現了Retrofit的CallBack<>,OnResponse()方法裡面去判斷網路請求正常的各種情況,onFailure()方法裡面則是網路有問題會走該回調。而OnResponse()回撥中也有可能網路請求失敗,根據response.raw().code()去判斷;然後你也可以根據異常出現的狀況去執行不同的UI,例如:

if(t instanceof SocketTimeoutException){
            //
        }else if(t instanceof ConnectException){
            //
        }else if(t instanceof RuntimeException){
           //
        }