之前在掘金胃珍、簡(jiǎn)書(shū)上看到很多篇介紹retrofit的文章蓝牲,但是一直沒(méi)有靜下心來(lái)研究這到底是什么東西傅蹂。最近時(shí)間比較充裕励稳,仔細(xì)了解了一下retrofit這個(gè)網(wǎng)絡(luò)請(qǐng)求工具的用法及實(shí)現(xiàn)原理∧硝澹現(xiàn)在做下簡(jiǎn)單的記錄犬金,以便日后重溫。
1六剥、Retrofit是什么晚顷?
Retrofit官網(wǎng)的介紹:A type-safe HTTP client for Android and Java ,一個(gè)類型安全的HTTP請(qǐng)求工具(現(xiàn)在對(duì)于類型安全理解不是太到位)疗疟。
Retrofit 是 Square 公司出品的默認(rèn)基于 OkHttp 封裝的一套 RESTful 網(wǎng)絡(luò)請(qǐng)求框架该默。Retrofit 的封裝可以說(shuō)是很強(qiáng)大,里面涉及到一堆的設(shè)計(jì)模式策彤,你可以通過(guò)注解直接配置請(qǐng)求 栓袖,可以使用不同 Json Converter 來(lái)序列化數(shù)據(jù)匣摘,同時(shí)提供對(duì) RxJava 的支持,使用 Retrofit + OkHttp + RxJava + Dagger2 可以說(shuō)是目前比較潮的一套框架裹刮,但是需要有比較高的門(mén)檻音榜。
2、基本用法
既然是網(wǎng)絡(luò)請(qǐng)求工具捧弃,那么最基本的功能就是發(fā)送HTTP請(qǐng)求赠叼,常用的GET、POST請(qǐng)求违霞。再有就是一些請(qǐng)求設(shè)置嘴办,比如超時(shí)時(shí)間、header等买鸽。
2.1 GET請(qǐng)求
先定義一個(gè)接口
/**
* Created by dell on 2016/9/7.
*
* 定義HTTP API使用接口
*
* 接口其實(shí)就是Retrofit對(duì)一個(gè)HTTP請(qǐng)求的描述
*/
public interface GitHubService {
@FormUrlEncoded //將會(huì)自動(dòng)將請(qǐng)求參數(shù)的類型調(diào)整為application/x-www-form-urlencoded
@POST("query") // 請(qǐng)求地址是 baseurl + query
Call<PostQueryInfo> search(@Field("type") String type, @Field("postid") String postid);//@Field 參數(shù)放到請(qǐng)求體
@GET("query")
/**
* @Query表示請(qǐng)求參數(shù)涧郊,將會(huì)以key=value的方式拼接在url后面
*
* 如果參數(shù)很多, 可以使用@QueryMap , getInfo(@QueryMap Map<String,String> map)
*/
Call<PostQueryInfo> getInfo(@Query("type") String type, @Query("postid") String postid);
@GET("/users/{user}")
Call<User> getFeed(@Path("user") String user); //@Path動(dòng)態(tài)的URL訪問(wèn)癞谒。像上面get請(qǐng)求中的{user}可以把它當(dāng)做一個(gè)占位符底燎,通過(guò)@Path("user")標(biāo)注的參數(shù)進(jìn)行替換
}
發(fā)送請(qǐng)求
/**
* 模擬GET 請(qǐng)求
*/
@OnClick(R.id.btn_get)
public void onClick() {
//retrofit是通過(guò)注解將HTTP轉(zhuǎn)化為java接口
Retrofit retrofit = new Retrofit.Builder()
.client(getNewClient())//設(shè)置不同的底層網(wǎng)絡(luò)庫(kù)
.baseUrl("http://www.kuaidi100.com/")
// .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
.addConverterFactory(GsonConverterFactory.create())//通過(guò)addConverterFactory來(lái)添加轉(zhuǎn)化器來(lái)完成數(shù)據(jù)轉(zhuǎn)化 ,把json數(shù)據(jù)轉(zhuǎn)成model對(duì)象
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<PostQueryInfo> call = service.getInfo("ems","1045861721780");
// try {
// User user = call.execute().body();//同步請(qǐng)求 刃榨,網(wǎng)絡(luò)請(qǐng)求一定要在子線程中完成弹砚,不能直接在UI線程執(zhí)行,不然會(huì)crash
// } catch (IOException e) {
// e.printStackTrace();
// }
//異步請(qǐng)求
call.enqueue(new Callback<PostQueryInfo>() {
@Override
public void onResponse(Call<PostQueryInfo> call, Response<PostQueryInfo> response) {
pb.dismiss();
if (response.isSuccessful()){
PostQueryInfo user = response.body();
Toast.makeText(RetrofitActivity.this, user.getMessage(), Toast.LENGTH_LONG).show();
Log.i(TAG, "user:" + user.getMessage());
}else{
ResponseBody errorBody = response.errorBody();
}
}
@Override
public void onFailure(Call<PostQueryInfo> call, Throwable t) {
pb.dismiss();
Log.e(TAG, "onFailure: " + t.getMessage());
}
});
// call.cancel();//取消請(qǐng)求
}
private OkHttpClient getNewClient() {
/**
* HttpLoggingInterceptor提供了4中控制打印信息類型的等級(jí)枢希,分別是NONE桌吃,BASIC,HEADERS苞轿,BODY茅诱,接下來(lái)分別來(lái)說(shuō)一下相應(yīng)的打印信息類型。
*
* NONE 沒(méi)有任何日志信息
* Basic 打印請(qǐng)求類型搬卒,URL瑟俭,請(qǐng)求體大小,返回值狀態(tài)以及返回值的大小
* Headers 打印返回請(qǐng)求和返回值的頭部信息契邀,請(qǐng)求類型摆寄,URL以及返回值狀態(tài)碼
* Body 打印請(qǐng)求和返回值的頭部和body信息
*
*/
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(this.getCacheDir(), cacheSize);
return new OkHttpClient.Builder()
// .addInterceptor(new CustomInterceptor())
.addInterceptor(logging)
.cache(cache)
// .readTimeout()
// .writeTimeout()
.connectTimeout(10, TimeUnit.SECONDS)
.build();
}
2.2 POST請(qǐng)求
在上一步驟中定義的接口,已經(jīng)包含了POST請(qǐng)求坯门,即用@POST這個(gè)注解修飾的方法微饥。
定義完接口之后,發(fā)送請(qǐng)求的方法和GET請(qǐng)求的方法類似
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.kuaidi100.com/")
.client(getNewClient())
.addConverterFactory(GsonConverterFactory.create())//添加轉(zhuǎn)換器古戴,gson數(shù)據(jù)轉(zhuǎn)成 model對(duì)象
.build();
/**
*
* Retrofit對(duì)象傳了一個(gè)GitHubService接口的Class對(duì)象欠橘,怎么又返回一個(gè)GitHubService對(duì)象呢?
*
* create方法重要就是返回了一個(gè)動(dòng)態(tài)代理對(duì)象
*
* 動(dòng)態(tài)代理對(duì)象 作用:當(dāng)你要調(diào)用某個(gè)類的方法前现恼,插入你想要執(zhí)行的代碼肃续。
*/
GitHubService service = retrofit.create(GitHubService.class);
/**
* service 是一個(gè)動(dòng)態(tài)代理對(duì)象黍檩,并不是GitHubService 的 implements對(duì)象。當(dāng)service對(duì)象調(diào)用search方法時(shí)始锚,執(zhí)行的是動(dòng)態(tài)代理方法建炫。
*
* 動(dòng)態(tài)代理發(fā)揮了它的作用,你看上去是調(diào)用了contributors方法疼蛾,其實(shí)此時(shí)Retrofit把Github接口翻譯成一個(gè)HTTP請(qǐng)求
*
* 后為你生成了一個(gè)Http請(qǐng)求肛跌,然后,你調(diào)用call.enqueue方法時(shí)就發(fā)送了這個(gè)請(qǐng)求察郁,然后你就可以處理Response的數(shù)據(jù)了
*/
Call<PostQueryInfo> call = service.search("ems", "1045861721780");
// PostBodyService service = retrofit.create(PostBodyService.class);
// Call<PostQueryInfo> call = service.getPostInfo(new PostInfo("ems","1045861721780"));
/**
* 同步請(qǐng)求 衍慎, 不能在main線程中執(zhí)行
*/
// try {
// PostQueryInfo info = call.execute().body();
// } catch (IOException e) {
// e.printStackTrace();
// }
/**
* 異步請(qǐng)求
*/
call.enqueue(new Callback<PostQueryInfo>() {
@Override
public void onResponse(Call<PostQueryInfo> call, Response<PostQueryInfo> response) {
pb.dismiss();
if (response.isSuccessful()){
String msg = response.body().getMessage();
Log.i(TAG, "msg:" + msg);
Toast.makeText(RetrofitActivity.this, msg, Toast.LENGTH_LONG).show();
}else{
ResponseBody requestBody = response.errorBody();
}
}
@Override
public void onFailure(Call<PostQueryInfo> call, Throwable t) {
pb.dismiss();
String msg = t.getMessage();
Log.e(TAG, "msg:" + msg);
}
});
2.3 自定義client : 可以設(shè)置超時(shí)時(shí)間、緩存等
private OkHttpClient getNewClient() {
/**
* HttpLoggingInterceptor提供了4中控制打印信息類型的等級(jí)皮钠,分別是NONE稳捆,BASIC,HEADERS麦轰,BODY乔夯,接下來(lái)分別來(lái)說(shuō)一下相應(yīng)的打印信息類型。
*
* NONE 沒(méi)有任何日志信息
* Basic 打印請(qǐng)求類型款侵,URL末荐,請(qǐng)求體大小,返回值狀態(tài)以及返回值的大小
* Headers 打印返回請(qǐng)求和返回值的頭部信息新锈,請(qǐng)求類型甲脏,URL以及返回值狀態(tài)碼
* Body 打印請(qǐng)求和返回值的頭部和body信息
*
*/
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(this.getCacheDir(), cacheSize);
return new OkHttpClient.Builder()
// .addInterceptor(new CustomInterceptor())
.addInterceptor(logging)
.cache(cache)
// .readTimeout()
// .writeTimeout()
.connectTimeout(10, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.client(getNewClient())//設(shè)置不同的底層網(wǎng)絡(luò)庫(kù)
.baseUrl("http://www.kuaidi100.com/")
// .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 適配器
.addConverterFactory(GsonConverterFactory.create())//通過(guò)addConverterFactory來(lái)添加轉(zhuǎn)化器來(lái)完成數(shù)據(jù)轉(zhuǎn)化 ,把json數(shù)據(jù)轉(zhuǎn)成model對(duì)象
.build();
2.4 轉(zhuǎn)換器 : 用什么方式解析服務(wù)端返回的數(shù)據(jù),支持gson妹笆、jackson块请、xml等
Gson: com.squareup.retrofit2:converter-gson:2.1.0
Moshi: com.squareup.retrofit2:converter-moshi:2.1.0
Jackson: com.squareup.retrofit2:converter-jackson:2.1.0
SimpleXML: com.squareup.retrofit2:converter-simplexml:2.1.0
ProtoBuf: com.squareup.retrofit2:converter-protobuf:2.1.0
Wire: com.squareup.retrofit2:converter-wire:2.1.0
如果上面的轉(zhuǎn)換器還不夠你使用的話,你可以通過(guò)自己實(shí)現(xiàn) Converter.Factory 來(lái)自定義轉(zhuǎn)換器拳缠。
2.5 添加header :有兩種方式墩新,靜態(tài)和動(dòng)態(tài)
2.5.1 靜態(tài)方式
通過(guò)@Headers注解。如:下面這段是官方文檔上的窟坐。
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("query")
Call<PostQueryInfo> getInfo(@Query("type") String type, @Query("postid") String postid);
或者也可以通過(guò)Interceptor來(lái)定義靜態(tài)請(qǐng)求頭
public class RequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("User-Agent", "Your-App-Name")
.header("Accept", "application/vnd.yourapi.v1.full+json")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
2.5.2 動(dòng)態(tài)添加 : 在定義的接口 方法中海渊,添加參數(shù)
@GET("/user")
Call<User> getUser(@Header("Authorization") String authorization , @Query("q") String name)
2.6 開(kāi)啟log日志
Retrofit官方提供了一個(gè)很方便查看日志的Interceptor,你可以控制你需要的打印信息類型狸涌,使用方法也很簡(jiǎn)單切省。
添加依賴:compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
HttpLoggingInterceptor提供了4中控制打印信息類型的等級(jí),分別是NONE帕胆,BASIC朝捆,HEADERS,BODY懒豹,接下來(lái)分別來(lái)說(shuō)一下相應(yīng)的打印信息類型芙盘。
* NONE 沒(méi)有任何日志信息
* Basic 打印請(qǐng)求類型驯用,URL,請(qǐng)求體大小儒老,返回值狀態(tài)以及返回值的大小
* Headers 打印返回請(qǐng)求和返回值的頭部信息蝴乔,請(qǐng)求類型,URL以及返回值狀態(tài)碼
* Body 打印請(qǐng)求和返回值的頭部和body信息
也是在自定義client中設(shè)置:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
client.addInterceptor(logging);
此后步驟參考2.3 中的getNewClient();
2.7 常用注解解釋:
@GET和@POST分別是get和post請(qǐng)求驮樊。括號(hào)里面的value值與上面.baseUrl組成完整的路徑
@Path動(dòng)態(tài)的URL訪問(wèn)薇正。像上面get請(qǐng)求中的{user}可以把它當(dāng)做一個(gè)占位符,通過(guò)@Path("user")標(biāo)注的參數(shù)進(jìn)行替換
@Query請(qǐng)求參數(shù)囚衔。無(wú)論是GET或POST的參數(shù)都可以用它來(lái)實(shí)現(xiàn)
@QueryMap請(qǐng)求參數(shù)使用Map集合挖腰。可以傳遞一個(gè)map集合對(duì)象
@FormUrlEncoded將會(huì)自動(dòng)將請(qǐng)求參數(shù)的類型調(diào)整為application/x-www-form-urlencoded 练湿,不能用于Get請(qǐng)求
@Body實(shí)體請(qǐng)求參數(shù)猴仑。顧名思義可以傳遞一個(gè)實(shí)體對(duì)象來(lái)作為請(qǐng)求的參數(shù),不過(guò)實(shí)體屬性要與參數(shù)名一一致(在測(cè)試代碼中肥哎,用這種方式傳遞參數(shù)辽俗,會(huì)報(bào)錯(cuò),就是@Body標(biāo)簽不能同時(shí)和@FormUrlEncoded篡诽、@Multipart標(biāo)簽同時(shí)使用)
@field :注解將每一個(gè)請(qǐng)求參數(shù)都存放至請(qǐng)求體中崖飘,還可以添加encoded參數(shù)
@fieldMap :POST請(qǐng)求參數(shù)有多個(gè)時(shí), 可以使用這個(gè)
@Multipart 文件上傳
@Url"對(duì)一個(gè)方法的參數(shù)進(jìn)行注解霞捡,表明這是個(gè)url ,可以傳遞一個(gè)完整的url地址
取消請(qǐng)求
在頁(yè)面退出時(shí)坐漏,要把正在進(jìn)行的請(qǐng)求取消,否則再回調(diào)后容易發(fā)生異常碧信。
單個(gè)請(qǐng)求可以調(diào)用call.cancel()
3、實(shí)現(xiàn)原理
動(dòng)態(tài)代理機(jī)制
參考文檔
1街夭、Android網(wǎng)絡(luò)訪問(wèn)庫(kù) - Retrofit學(xué)習(xí)(1)基礎(chǔ)
2砰碴、Android 網(wǎng)絡(luò)開(kāi)源庫(kù)-Retrofit(一)簡(jiǎn)單介紹
3、Retrofit 2.0:迄今為止最大的更新最好的Android HTTP客戶端庫(kù)
4板丽、Retrofit用法詳解(有各種常見(jiàn)用法樣例)
5呈枉、Retrofit源碼1: 為什么寫(xiě)一個(gè)interface就可以實(shí)現(xiàn)http請(qǐng)求
6、關(guān)于Retrofit的用法:Retrofit 是什么埃碱,1猖辫、Retrofit 怎么用,2砚殿、Retrofit的原理是什么
7啃憎、使用Retrofit2.0+OkHttp3.0實(shí)現(xiàn)緩存處理
8、 Android網(wǎng)絡(luò)請(qǐng)求庫(kù) - Say hello to retrofit
9似炎、Retrofit 官網(wǎng)
10辛萍、我們真的需要使用RxJava+Retrofit嗎悯姊?
11、拆輪子系列:拆 Retrofit
12贩毕、「Android技術(shù)匯」Retrofit2 源碼解析和案例說(shuō)明