一、亮點介紹:
自從鴻蒙手機版發布以來,我就一直在做移植的工作,將安卓代碼移植到鴻蒙系統上。Retrofit是安卓系統上一款優秀的網絡請求框架,鴻蒙系統並沒有類似的網絡請求框架。於是,我決定實現一套鴻蒙版的Retrofit。
蒹葭(JianJia)是一款鴻蒙系統上的網絡請求框架,其實就是將安卓的Retrofit移植到鴻蒙系統上,我將鴻蒙版的Retrofit命名為蒹葭(JianJia)。蒹葭不僅能實現Retrofit的功能,還會提供一些Retrofit沒有的功能。Retrofit不支持動態替換域名,國內的應用一般都是有多個域名的,蒹葭支持動態替換域名。Retrofit並不能夠直接添加攔截器,只能通過okhttp來添加攔截器,蒹葭會支持添加攔截器。
二、Demo編譯及效果呈現如下:
注:文檔附件在最下面
三、源碼:
https://gitee.com/zhongte/JianJia
要想讀懂源碼,需要具備以下技能。
熟悉okhttp的常見用法
熟悉面向接口編程、反射、泛型、注解
熟悉構造者模式、適配器模式、工廠模式、策略模式、靜態代理、動態代理、責任鏈模式等設計模式
四、用法,用法跟Retrofit一樣
蒹葭提供了一系列的注解,在進行網絡請求的時候,就需要用到這些注解。
4.1 GET注解
創建接口,在方法里面使用GET注解,GET注解用於標識這是一個GET請求,方法的返回值是Call對象,泛型是ResponseBody,其實泛型也可以是具體的實體對象,這個后面再說。蒹葭如何完成網絡請求?使用構造者模式創建jianjia對象,baseUrl就是域名,在創建jianjia對象的時候就必須指定域名。調用create方法來生成接口的實例,調用wan.getBanner().enqueue來執行網絡請求,請求成功就會回調onResponse方法,請求失敗就會回調onFailure方法
public interface Wan {
@GET("banner/json")
Call<ResponseBody> getBanner();
}
JianJia jianJia = new JianJia.Builder()
.baseUrl("https://www.wanandroid.com")
.build();
Wan wan = jianJia.create(Wan.class);
wan.getBanner().enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String json = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
LogUtils.info("yunfei", t.getMessage());
}
});
4.2 BaseUrl注解
國內的應用一般都是有多個域名的,BaseUrl注解可以對某個接口設置單獨的域名。
public interface Wan {
@BaseUrl("https://api.apiopen.top")
@GET("getJoke")
Call<ResponseBody> getJoke(@QueryMap Map<String, String> param);
}
4.3 Path注解
Path注解在路徑中替換指定的參數值,定義下面的方法。可以看到我們定義了一個getArticle方法,方法接收一個page參數,並且我們的@GET注解中使用{page}聲明了訪問路徑,這里你可以把{page}當做占位符,而實際運行中會通過@Path("page")所標注的參數進行替換。
public interface Wan {
@GET("article/list/{page}/json")
Call<ResponseBody> getArticle(@Path("page") int page);
}
4.4 Query注解
Query注解用於給get請求添加請求參數,被Query注解修飾的參數類型可以是數組、集合、字符串等
public interface Wan {
@GET("wxarticle/list/405/1/json")
Call<ResponseBody> search(@Query("k") String k);
@GET("wxarticle/list/405/1/json")
Call<ResponseBody> search(@Query("k") String... k);
@GET("wxarticle/list/405/1/json")
Call<ResponseBody> search(@Query("k") List<String> k);
}
4.5 QueryMap注解
QueryMap注解以map的形式添加查詢參數,被QueryMap注解修飾的參數類型必須是Map對象
public interface Wan {
@GET("wxarticle/list/405/1/json")
Call<ResponseBody> search(@QueryMap Map<String, String> param);
}
4.6 SkipCallbackExecutor注解
在鴻蒙系統下,默認會將服務端的響應回調到主線程,如果在方法上使用SkipCallbackExecutor注解,那就不會將服務端的結果回調到主線程
public interface Wan {
@SkipCallbackExecutor
@GET("wxarticle/list/405/1/json")
Call<ResponseBody> search(@QueryMap Map<String, String> param);
}
4.7 FormUrlEncoded注解和Field注解
FormUrlEncoded注解用於發送一個表單請求,使用該注解必須在方法的參數添加Field注解,被Field注解修飾的參數類型可以是數組、集合、字符串等
public interface Wan {
@POST("user/login")
@FormUrlEncoded
Call<ResponseBody> login(@Field("username") String username, @Field("password") String password);
}
4.8 FormUrlEncoded注解和FieldMap注解
有時候表單的參數會比較多,如果使用Field注解,方法的參數就會比較多,此時就可以使用FieldMap注解,FieldMap注解以map的形式發送一個表單請求。如果被FieldMap注解修飾的參數不是Map類型,就會拋異常。如果Map的鍵值對為空,也會拋異常。
public interface Wan {
@POST("user/login")
@FormUrlEncoded
Call<ResponseBody> login(@FieldMap Map<String, String> map);
}
4.9 Body注解
服務端會要求端上把json字符串作為請求體發給服務端。此時就可以使用Body注解定義的參數可以直接傳入一個實體類,內部會把該實體序列化並將序列化后的結果直接作為請求體發送出去。
如果被Body注解修飾的參數的類型是RequestBody對象,那調用者可以不添加數據轉換器,內部會使用默認的數據轉換器
如果被Body注解修飾的參數的類型不是RequestBody對象,是一個具體的實體類,那調用者需要自定義一個類,並且繼承Converter.Factory
public interface Wan {
/**
* 被Body注解修飾的參數的類型是RequestBody對象,那調用者可以不添加數據轉換器,內部會使用默認的數據轉換器
*
* @param body
* @return
*/
@POST("user/register")
Call<ResponseBody> register(@Body RequestBody body);
/**
* 被Body注解修飾的參數的類型不是RequestBody對象,是一個具體的實體類,那調用者需要自定義一個類,並且繼承Converter.Factory
*
* @param user
* @return
*/
@POST("user/register")
Call<ResponseBody> register(@Body User user);
}
4.10 Url注解
Url注解用於添加接口的完整地址。在Retrofit里面,如果接口的域名與創建retrofit對象指定的域名不相同,那就會使用Url注解來解決問題。在蒹葭里面同樣可以使用Url注解來解決問題,但蒹葭還提供了BaseUrl來解決該問題。
public interface Wan {
@GET()
Call<ResponseBody> getArticle(@Url String url);
}
4.11 Headers注解
Headers注解是作用於方法上的注解,用於添加一個或多個請求頭。
public interface Wan {
@Headers("Cache-Control: max-age=640000")
@GET("/")
Call<ResponseBody> getArticle(@Url String url);
@Headers({
"X-Foo: Bar",
"X-Ping: Pong"
})
@GET("/")
Call<ResponseBody> getArticle(@Url String url);
}
4.12 Header注解
Header注解是作用於參數上的注解,用於添加請求頭
public interface Wan {
@GET()
Call<ResponseBody> foo(@Header("Accept-Language") String lang);
}
4.13 HeaderMap注解
HeaderMap注解是作用於參數上的注解,以map的形式添加請求頭,map中每一項的鍵和值都不能為空,否則會拋異常
public interface Wan {
@GET("/search")
Call<ResponseBody> list(@HeaderMap Map<String, String> headers);
}
五、添加數據轉換器
之前我們在接口里面定義方法的時候,方法的返回值時Call對象,泛型是ResponseBody。在這種情況下,服務端返回給端上的數據就會在ResponseBody里面,端上需要手動解析json,將json解析成一個實體類。
其實,我們沒必要手動解析json,可以讓gson幫我們解析json。蒹葭支持添加數據轉換器,在創建對象的時候添加數據轉換器,也就是把gson添加進來。在onResponse方法里面就可以直接得到實體類對象了,gson幫我們把json解析成了一個實體類。
public interface Wan {
@GET("banner/json")
Call<Banner> getBanner();
}
JianJia jianJia = new JianJia.Builder()
.baseUrl("https://www.wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
Wan wan = jianJia.create(Wan.class);
wan.getBanner().enqueue(new Callback<Banner>() {
@Override
public void onResponse(Call<Banner> call, Response<Banner> response) {
try {
if (response.isSuccessful()) {
Banner banner = response.body();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<Banner> call, Throwable t) {
LogUtils.info("yunfei", t.getMessage());
}
});
六、蒹葭的后續工作
6、1 上傳文件也需要使用注解,目前蒹葭還沒有上傳文件的注解,上傳文件的注解正在開發當中。
6、2 蒹葭目前還不支持直接添加攔截器,后續會把攔截器的功能加上。
6、3 網絡請求框架的二次封裝,網絡請求框架的二次封裝應當隔離掉第三方網絡框架,如果將來有更好的網絡框架,或者公司要求使用公司自研的網絡框架,那就可以很容易的替換掉之前的網絡框架,而不需要大改特改。
6、4 由於疫情原因,我估計得在北京過年了,無法回老家過年。所以可以趁放假的時候,給大家錄視頻。手把手的叫大家實現自己的網絡框架,希望能夠幫助大家理解蒹葭的實現原理。當然了,蒹葭的實現原理跟Retrofit的原理是一模一樣的。本身蒹葭就是從Retrofit移植過來的,只不過蒹葭可以運行在鴻蒙系統上。
原文鏈接:https://developer.huawei.com/consumer/cn/forum/topic/0204470884054220049?fid=0101303901040230869
原作者:義薄雲天小關羽
