1. 程式人生 > >安卓專案實戰之強大的網路請求框架okGo使用詳解(二):深入理解Callback之自定義JsonCallback

安卓專案實戰之強大的網路請求框架okGo使用詳解(二):深入理解Callback之自定義JsonCallback

前言

JSON是一種取代XML的資料結構,和xml相比,它更小巧但描述能力卻不差,由於它的小巧所以網路傳輸資料將減少更多流量從而加快了傳輸速度,目前客戶端伺服器返回的資料大多都是基於這種格式的,相應的我們瞭解的關於json的解析工具主要有兩個:Gson(Google官方出的)和fastjson(阿里巴巴工程師開發的),本篇開篇主要針對Gson解析作簡要回顧。

json解析之Gson工具使用簡要回顧

json有json物件和json陣列之分,物件以“{”(左括號)開始,“}”(右括號)結束,陣列以“[”(左中括號)開始,“]”(右中括號)結束,如下:

{
	"code":0,
	"msg":"請求成功",
	"data":{
		"id":123456,
		"name":"張三",
		"age":18
	}	
}
[
	{
		"id":123456,
		"name":"張三",
		"age":18
	},
	{
		"id":123456,
		"name":"張三",
		"age":18
	},
	{
		"id":123456,
		"name":"張三",
		"age":18
	}
]

Gson工具完成java物件和json格式字串之前的轉換如下:( toJson 和 fromJson )

// 使用new方法建立Gson物件
Gson gson = new Gson();

// toJson 將bean物件轉換為json字串
String jsonStr = gson.toJson(user, User.class);

// fromJson 將物件格式的json字串轉為bean物件
Student user= gson.fromJson(jsonStr, User.class);

// 將集合轉成json字串
String jsonStr2 = gson.toJson(list);

// 將陣列格式的json轉化成List時需要使用到TypeToken getType()**
List<User> retList = gson.fromJson(jsonStr2,new TypeToken<List<User>>(){}.getType());

一般情況下我們都會根據json字串定義對應的資料實體類,通常遵循如下的規則:
1,看到JSON結構裡面有{ }你就定義一個類,看到[ ]你就定義一個List即可,最後只剩下最簡單的如String、int等基本型別直接定義就好。,
2,內部巢狀的類,請使用public static class className { }。
3,類內部的屬性名,必須與JSON串裡面的Key名稱保持一致。

統一的伺服器返回資料實體類的定義

通過和公司後臺協商,統一了伺服器端返回json的格式,如介面返回的資料分為兩類:

{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}

我們真正需要的是data所包含的資料,而code只使用一次,message則幾乎不用。如果Gson不支援泛型或不知道Gson支援泛型的同學一定會這麼定義實體類:

public class UserResponse {
    public int code;
    public String message;
    public User data;
}

當使用其它介面的時候又得根據該介面返回的json字串重新定義一個XXXResponse的實體類,並將data的型別改成XXX,如下:

public class PersonResponse {
    public int code;
    public String message;
    public Person data;
}

很明顯code,和message被重複定義了多次,通過泛型的話我們可以將code和message欄位抽取到一個Result的類中,這樣我們只需要編寫data欄位所對應的實體類即可,更專注於我們的業務邏輯。如:

public class Result<T> {
    public int code;
    public String message;
    public T data;
}

那麼對於data欄位是User時則可以寫為 Result< User > ,當是個列表的時候為 Result<List< User >>,其它同理。

okGo之自定義jsonCallback

上面也說了,json現在是主流的兩端資料傳輸方式,因此定義一個針對伺服器端json字串的返回做解析的回撥還是十分有必要的。
我們都知道網路的資料是以流的形式進行傳遞的,我們在解析資料的時候,把流先解析成JSON字串,然後在通過Gson解析成物件這個過程是沒必要的,如果大量的請求發生,是不是就有大量的資料要被轉成JSON字串,資料量小還行,一旦較大,連請求都發生OOM了,這並不是我們希望看到的,那麼有沒有辦法能直接把流解析成物件呢,不要轉成中間的字串物件,當然是可以的,Gson框架也為我們提供了這個方法,解析程式碼如下:
在這裡插入圖片描述
在網路請求框架okGo使用詳解(一)的文章中對於自定義callback的深入理解中我們知道,定義一個callback我們可以通過繼承AbsCallback來實現,主要重寫裡面的convertResponse方法來提供一種實現,完成伺服器端返回資料和泛型指定格式之間的轉換,okGo框架內建的幾種callback都分別額外定義了一個實現convert介面的轉換器類,然後在自定義callback類的convertResponse方法中其實又呼叫了轉換器物件的convertResponse方法完成的轉換,其實我們完全可以不額外定義轉換器類,直接將轉換的解析邏輯程式碼放在自定義callback類的convertResponse方法中,如下:
在這裡插入圖片描述Retrofit預設的GsonConvert的核心也就是上面這幾行程式碼實現。
然後自定義的jsonCallback就可以這樣來使用了:
在這裡插入圖片描述請求方式1對應的url返回的json格式為:json物件字串,即以 { 開始以 } 結尾,Login是根據該結果封裝的實體類,底層其實藉助Gson以流的方式完成的json字串和物件之間的轉化。
請求方式2對應的url返回的json格式為:json陣列字串,即以 [ 開始以 ] 結尾,List< ServerModel >是根據該結果封裝的實體類,底層其實藉助Gson以流的方式完成的json字串和集合之間的轉化。

注意:
如果上面這點程式碼在執行時還是有問題,強烈建議不要自定義Callback了,直接用框架內建的StringCallback,寫法極其簡單,就這樣,再次強調第一行的泛型一定要寫String,才能用StringCallback,否則無法編譯通過。

伺服器返回json有固定資料格式的情況下對JsonCallback的再優化

場景: 大多數情況,伺服器返回的資料格式都是有個統一的規範的,就像我們上面gson簡介末尾說的那樣,伺服器端返回給我們的json格式規定如下形式:

{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}

那麼我們可以使用泛型,分離基礎包裝與實際資料,這樣子需要定義兩個javabean,一個全專案通用的LzyResponse,一個單純的業務模組需要的資料,具體的定義如下例所示:
在這裡插入圖片描述對於這種方式,我們在建立JsonCallback的時候,需要這麼將LzyResponse< ServerModel>或LzyResponse<List< ServerModel>>整體作為一個泛型傳遞,相當於傳遞了兩層,泛型中又包含了一個泛型,使用方法如下:
在這裡插入圖片描述那麼JsonCallback中就需要做改動了,詳細的原理就不說了,直接上程式碼,詳細看註釋:
在這裡插入圖片描述(上圖中的0以及其他的錯誤碼和錯誤資訊提示根據自己專案指定的錯誤碼和錯誤描述來修改)
一般來說伺服器會和客戶端約定一個數表示請求資料成功,上面示例中假設code為0時代表獲取資料成功,這裡的請求資料成功指的是json字串中data對應的部分有資料獲取到才算成功而非一次網路請求的成功與否,例如當我們執行使用者登入操作時,正常情況下登入成功伺服器端會返回給我們使用者的資訊以物件的形式在data之後,如下:

{"code":"0","message":"success","data":{"name":"gpf","sex":"man"}}

但是當用戶名或者密碼輸入錯誤時,伺服器端返回的json格式如下,此時網路請求是正常的,但是出錯了,沒有獲取到返回的具體資料,因此data資料部分為空:

{"code":"111","message":"使用者名稱或者密碼錯誤!"}

很明顯上面的這種情況我們應該回調onError方法的,但是如果我們沒有在自定義的JsonCallback的convertResponse方法中對成功獲取到資料中包含錯誤情況的判斷邏輯時(也就是上面請求成功並且有資料時拿到code的值進行判斷的那部分程式碼),仍會回撥onSuccess方法,然後我們在onSuccess回撥中再拿到錯誤碼code,來顯示錯誤提示,這樣做的結果是,把無論正確的還是錯誤的資料,都交給了onSuccess處理,在使用上不太友好,邏輯分層混亂。

優化後的方式,這種方式不僅可以正確的解析資料,而且能在解析的過程中判斷錯誤碼,並根據不同的錯誤碼丟擲不同的異常(這裡丟擲異常並不會導致okgo掛掉,而是以異常的形式通知okgo回撥onError,並且將該異常以引數的形式傳遞到onError,這樣做後,在onSuccess中處理的一定是成功的資料,在onError中處理的一定是失敗的資料,達到了友好的邏輯分層。

異常解析onError方法對應修改如下:(根據自己專案指定的錯誤碼和錯誤描述來修改)

在這裡插入圖片描述在onError方法中我們使用操作符instanceof區分OkGo自己的異常和我們自定義Callback時使用者自定義丟擲的異常

注意:以上這種優化方法只適用於伺服器返回此固定資料格式的情況,同時我也推薦服務端對返回的資料做一次統一包裝,就像上面的示例一樣,用code和msg包一層。