android使用gson和泛型解析伺服器回撥的封裝

Jadyli1發表於2017-06-09

android使用gson和泛型解析伺服器回撥的封裝

概述

前面我們介紹瞭如何使用Gson解析和建立json,這裡我們繼續介紹如何使用泛型來封裝伺服器回撥。
本文主要的技術點有

  • 使用類的泛型和方法的泛型
  • 結合gson封裝伺服器json回撥

1 建立通用的回撥Model

以這個伺服器回撥為例。

{
  "server_time": 1497611816029,
  "data": {
    "name": "jady",
    "age": 12
  },
  "success": true
}

這個json裡面server_timesuccess的型別是固定不變的,但是data的型別卻會經常改變,比如這裡傳回來的是User,另一個介面傳回來的可能就是Student了。
首先建立對應的類:

public class ServerCallbackModel<T> {

   private long server_time;
    private T data;
    private boolean success;
    private String err_code;
    private String message;

    public long getServer_time() {
        return server_time;
    }

    public void setServer_time(long server_time) {
        this.server_time = server_time;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getErr_code() {
        return err_code;
    }

    public void setErr_code(String err_code) {
        this.err_code = err_code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

2 封裝伺服器回撥

這裡我們寫的伺服器回撥是基於已經拿到伺服器回撥的json字串。

2.1 回撥的抽象基類:

public abstract class HttpCallback<T> {

    public static final String TAG = "HttpCallback";

    protected Type genericityType;

    public HttpCallback() {
        Type genericSuperclass = getClass().getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
            this.genericityType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
        } else {
            this.genericityType = Object.class;
        }
    }

    public abstract void onResolve(T t);

    public abstract void onFailed(String err_code, String message);

    public Type getGenericityType() {
        return genericityType;
    }
}

這裡定義了一個構造方法,兩個抽象方法,我們先看構造方法,

Type genericSuperclass = getClass().getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
     this.genericityType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
} else {
     this.genericityType = Object.class;
}

Type: Java中所有型別的公共高階介面。它們包括原始型別、引數化型別、陣列型別、型別變數和基本型別。
getGenericSuperclass()獲得帶有泛型的父類: 獲得帶有泛型的父類。
ParameterizedType: 引數化型別,即泛型。
getActualTypeArguments:獲取引數化型別的陣列,泛型引數可能有多個

getActualTypeArguments()[0]得到了泛型的第一個引數T的型別,賦值給代表當前類泛型型別的成員變數genericityType。如果不是引數化型別,比如在接收伺服器回撥的地方沒有加泛型型別,那麼我們就給genericityType賦值Object的class物件。

genericityType是用來在後面作為Gson解析的型別引數的。

再來看看抽象方法:

public abstract void onResolve(String json);
public abstract void onFailed(String err_code, String message);

聰明的你一定發現了沒有onSuccess(),別急,看到後面你就明白了。
onResolve()就是用來解析json的了,子類需要重寫它來具體解析。
onFailed是錯誤回撥。

2.2 針對剛才伺服器物件封裝的抽象子類

public abstract class ServerCallback<T, V> extends HttpCallback<T>{
    @Override
    public void onResolve(String json) {
        boolean returnJson = false;
        if (genericityType instanceof Class) {
            switch (((Class) genericityType).getSimpleName()) {
                case "Object":
                case "String":
                    returnJson = true;
                    break;
                default:
                    break;
            }
        }

        if (returnJson) {
            onSuccess((V) json);
        } else {
            T t = (new Gson()).fromJson(json, genericityType);
            if (t instanceof ServerCallbackModel) {
                ServerCallbackModel<V> callbackData = (ServerCallbackModel) t;
                V result = callbackData.getData();
                if (callbackData.isSuccess()) {
                    this.onSuccess(result);
                } else {
                    onFailed(callbackData.getErr_code(), callbackData.getMessage());
                }
            } else {
                onSuccess((V) t);
            }
        }
    }

    public void onFailed(String error_code, String error_message) {
        if (enableShowToast()) {
            Toast.makeText(HttpManager.mContext, error_message, Toast.LENGTH_SHORT).show();
        }
        onFailure(error_code, error_message);
    }

    public abstract void onSuccess(V data);

    public abstract void onFailure(String error_code, String error_message);

    public boolean enableShowToast() {
        return false;
    }
}

首先,我們定義了一個抽象的帶兩個型別引數的泛型類,第一個引數是Gson對映的外層物件,比如可以傳入我們剛才定義的ServerCallbackModel,第二個引數就是外層物件中的資料了,比如ServerCallbackModel中的data。

然後來分析下類中的方法,先來看看非抽象的方法:

public void onResolve(String json)

這個方法用來解析伺服器回撥,引數json就是伺服器回撥的json字串。
首先判斷泛型型別genericityType是不是Class型別的,然後判斷genericityType的名字是否為String或者Object,是則直接在成功回撥中傳入json字串。
如果不是,則用Gson將json解析出來,實參就是genericityType。此時解析出來的型別如果是我們剛才定義的ServerCallbackModel,如果伺服器返回成功,那就將data返回,data的型別就是這個類的第二個型別引數,也是onSuccess的形參型別,我們最終需要的資料。
如果伺服器返回失敗,會將錯誤傳遞給onFailed。

protected void onFailed(String error_code, String error_message)

這個方法主要是攔截錯誤,用於統一處理所有請求的錯誤。預設只判斷是否需要直接彈toast顯示錯誤資訊。

public boolean enableShowToast() 

我們可以考慮是否直接將錯誤資訊彈窗提示出來,只需要重寫enableShowToast方法,返回true即可。

再來看看抽象方法,這個就是我們最終在具體的業務層需要重寫的方法了。

public abstract void onSuccess(V data);

伺服器返回成功的資訊後,資料會傳遞到這裡。

public abstract void onFailure(String error_code, String error_message);

錯誤經過onFailed攔截後,會傳遞到這裡。

3 使用封裝的回撥

HttpCallback callback = new TmpCallback<ServerCallbackModel<User>,User>() {

   @Override
   public void onSuccess(User data) {

   }

   @Override
   public void onFailure(String error_code, String error_message) {

   }
};
API.testGet(callback);

剛才你看到ServerCallback可能還不理解為什麼這個泛型要有兩個引數,看到這裡你應該就秒懂了,其實伺服器傳回來的正確錯誤什麼的,我們並不想每個呼叫的地方都去判斷,我們這裡想要的只是最終的資料。
本文的Demo地址是我的一個開源庫,這個庫已經經過了我們公司產品大量使用者的考驗,歡迎starfork,也歡迎大家提issue

RetrofitClient: https://github.com/Jadyli/RetrofitClient

裡面的封裝基本都是按照本文的思路寫的,當然也可以有所變化,比如有些介面沒有success之類的,直接就是上資料,那就可以使用CommonCallback

相關文章