Retrofit學(xué)習(xí)筆記
*翻譯自官方文檔(https://square.github.io/retrofit/)
QuickstartRetrofit主要做的事情就是把HTTP API轉(zhuǎn)換成Java接口。
public?interface?GitHubService?{
??@GET("users/{user}/repos")
??Call<List<Repo>>?listRepos(@Path("user")?String?user);
}
Retrofit類產(chǎn)生一個(gè)GitHubService接口的實(shí)現(xiàn)。
Retrofit?retrofit?=?new?Retrofit.Builder()
????.baseUrl("https://api.github.com/")
????.build();
GitHubService?service?=?retrofit.create(GitHubService.class);
從GitHubService中產(chǎn)生的每個(gè)Call對(duì)象都可以用來向遠(yuǎn)程Web服務(wù)器產(chǎn)生一個(gè)同步或異步的HTTP請(qǐng)求。
請(qǐng)求方法
支持HTTP、GET、POST等所有HTTP方法。
@GET("users/list")
//?加上請(qǐng)求參數(shù)
@GET("users/list?sort=desc")?
URL填充
支持動(dòng)態(tài)URL,可以通過{}來包裹需要填充的字符串(數(shù)字和字母)。在參數(shù)中用@Path來指定。
@GET("group/{id}/users")
Call<List<User>>?groupList(@Path("id")?int?groupId);
也支持請(qǐng)求參數(shù)(用@Query指定)
@GET("group/{id}/users")
Call<List<User>>?groupList(@Path("id")?int?groupId,?@Query("sort")?String?sort);
在復(fù)雜查詢中可以使用Map(用@QueryMap指定)
@GET("group/{id}/users")
Call<List<User>>?groupList(@Path("id")?int?groupId,?@QueryMap?Map<String,?String>?options);
請(qǐng)求體(Body)
用@Body指定request body。
@POST("users/new")
Call<User>?createUser(@Body?User?user);
該對(duì)象會(huì)根據(jù)Retrofit實(shí)例中指定的converter轉(zhuǎn)換。(解釋:例如如果指定使用Gson,就會(huì)使用Gson對(duì)該對(duì)象進(jìn)行轉(zhuǎn)換,即轉(zhuǎn)換成json。)如果沒有指定converter,只能使用RequestBody。(?)
表單和multipart
使用@FormUrlEncoded和@Multipart。如
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
@Multipart
@PUT("user/photo")
Call<User>?updateUser(@Part("photo")?RequestBody?photo,?@Part("description")?RequestBody?description);
這部分用得比較少,不細(xì)看了。關(guān)于Multipart和Form-urlencoded的區(qū)別可以看https://blog.csdn.net/lihefei_coder/article/details/99606386
請(qǐng)求頭
用@Header標(biāo)識(shí)。
@Headers("Cache-Control:?max-age=640000")
@GET("widget/list")
Call<List<Widget>>?widgetList();
@Headers({
????"Accept:?application/vnd.github.v3.full+json",
????"User-Agent:?Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User>?getUser(@Path("username")?String?username);
請(qǐng)求頭也可以是動(dòng)態(tài)的。當(dāng)值為空時(shí),會(huì)忽略該請(qǐng)求頭。否則將會(huì)對(duì)其值調(diào)用toString。
@GET("user")
Call<User>?getUser(@Header("Authorization")?String?authorization)
也可以使用Map
@GET("user")
Call<User>?getUser(@HeaderMap?Map<String,?String>?headers)
同步和異步
Call實(shí)例可以同步或異步執(zhí)行,每個(gè)實(shí)例只能使用一次,但clone()會(huì)產(chǎn)生一個(gè)可以使用的新實(shí)例。
在安卓中 ,回調(diào)會(huì)在主線程中執(zhí)行。在JVM中,回調(diào)會(huì)在執(zhí)行請(qǐng)求的同一個(gè)線程中執(zhí)行。
Retrofit配置使用Gson:
Retrofit?retrofit?=?new?Retrofit.Builder()
????.baseUrl("https://api.github.com/")
????.addConverterFactory(GsonConverterFactory.create())
????.build();
GitHubService?service?=?retrofit.create(GitHubService.class);
第一行代碼中的實(shí)例(SunnyWeather)目錄結(jié)構(gòu)

其中PlaceService、WeatherService是綁定Retrofit的接口,ServiceCreator用于創(chuàng)建對(duì)應(yīng)Service的retrofit實(shí)例,SunnyWeatherNetwort則是供Repository調(diào)用的方法。
PlaceService
WeatherService和PlaceService類似,不重復(fù)講了。
package?com.sunnyweather.android.logic.network
import?com.sunnyweather.android.SunnyWeatherApplication
import?com.sunnyweather.android.logic.model.PlaceResponse
import?retrofit2.Call
import?retrofit2.http.GET
import?retrofit2.http.Query
interface?PlaceService?{
????@GET("v2/place?token=${SunnyWeatherApplication.TOKEN}&lang=zh_CN")
????fun?searchPlaces(@Query("query")?query:?String):?Call<PlaceResponse>
}
主要是用GET方法,${SunnyWeatherApplication.TOKEN}表示從配置文件中取token的值。
ServiceCreator
用來創(chuàng)建Retrofit實(shí)例。
package?com.sunnyweather.android.logic.network
import?retrofit2.Retrofit
import?retrofit2.converter.gson.GsonConverterFactory
object?ServiceCreator?{
????private?const?val?BASE_URL?=?"https://api.caiyunapp.com/"
????private?val?retrofit?=?Retrofit.Builder()
????????.baseUrl(BASE_URL)
????????.addConverterFactory(GsonConverterFactory.create())
????????.build()
????fun?<T>?create(serviceClass:?Class<T>):?T?=?retrofit.create(serviceClass)
????inline?fun?<reified?T>?create():?T?=?create(T::class.java)
}
inline那一行是泛型優(yōu)化,暫時(shí)沒搞懂。
SunnyWeatherNetwork
主要用了掛起函數(shù)(suspend)實(shí)現(xiàn)異步。
package?com.sunnyweather.android.logic.network
import?retrofit2.Call
import?retrofit2.Callback
import?retrofit2.Response
import?kotlin.coroutines.resume
import?kotlin.coroutines.resumeWithException
import?kotlin.coroutines.suspendCoroutine
object?SunnyWeatherNetwork?{
????private?val?weatherService?=?ServiceCreator.create(WeatherService::class.java)
????suspend?fun?getDailyWeather(lng:?String,?lat:?String)?=?weatherService.getDailyWeather(lng,?lat).await()
????suspend?fun?getRealtimeWeather(lng:?String,?lat:?String)?=?weatherService.getRealtimeWeather(lng,?lat).await()
????private?val?placeService?=?ServiceCreator.create(PlaceService::class.java)
????suspend?fun?searchPlaces(query:?String)?=?placeService.searchPlaces(query).await()
????private?suspend?fun?<T>?Call<T>.await():?T?{
????????return?suspendCoroutine?{?continuation?->
????????????enqueue(object?:?Callback<T>?{
????????????????override?fun?onResponse(call:?Call<T>,?response:?Response<T>)?{
????????????????????val?body?=?response.body()
????????????????????if?(body?!=?null)?continuation.resume(body)
????????????????????else?continuation.resumeWithException(RuntimeException("response?body?is?null"))
????????????????}
????????????????override?fun?onFailure(call:?Call<T>,?t:?Throwable)?{
????????????????????continuation.resumeWithException(t)
????????????????}
????????????})
????????}
????}
}
