Retrofit
Retrofit最新版引入
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
}
除了Retrofit之外不引入其他依賴。
網(wǎng)絡(luò)請(qǐng)求:
interface ApiService {
@GET("getUserData")
fun getUserData():Call<ResponseBody>
}
fun netRequest(){
// 網(wǎng)絡(luò)請(qǐng)求地址格式要寫對(duì)欣簇,否則crash
// 需要網(wǎng)絡(luò)權(quán)限否則crash
val retrofit = Retrofit.Builder()
.baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
.build()
val service = retrofit.create(ApiService::class.java)
val call = service.getUserData()
call.enqueue(object :Callback<ResponseBody>{
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Log.d(TAG, "onFailure: $t")
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
val userBean = response.body()?.string()
Log.d(TAG, "onResponse: $userBean")
}
})
}
Retrofit是建立在OkHttp只上的一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝庫(kù)巾钉,內(nèi)部依靠OkHttp來完成網(wǎng)絡(luò)請(qǐng)求翘狱。API通過interface來聲明,配置API路徑砰苍、請(qǐng)求方式潦匈、請(qǐng)求參數(shù)、返回值類型等配置赚导。getUserData()請(qǐng)求結(jié)果是一個(gè)json格式的字符串历等,返回類型定義為Call<ResponseBody>,okhttp3.ResponseBody辟癌,retrofit2.Call是Retrofit對(duì)okhttp3.Call包裝寒屯。
converter-gson
上面返回的是json格式,我們希望返回的是Bean對(duì)象黍少;使用庫(kù)進(jìn)行反序列化寡夹;
庫(kù)引入:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
請(qǐng)求邏輯:
interface ApiService {
@GET("getUserData")
fun getUserData():Call<UserBean>
}
data class UserBean(val status:String,val msg:String,val data:Data)
data class Data(val userName:String,val userAge:String)
fun netRequest(){
// 網(wǎng)絡(luò)請(qǐng)求地址格式要寫對(duì),否則crash
// 需要網(wǎng)絡(luò)權(quán)限否則crash
val retrofit = Retrofit.Builder()
.baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(ApiService::class.java)
val call = service.getUserData()
call.enqueue(object :Callback<UserBean>{
override fun onFailure(call: Call<UserBean>, t: Throwable) {
Log.d(TAG, "onFailure: $t")
}
override fun onResponse(call: Call<UserBean>, response: Response<UserBean>) {
val userBean = response.body()
Log.d(TAG, "onResponse: $userBean")
}
})
}
adapter-rxjava2
如果不想看到Call<UserBean>,通過RxJava方式進(jìn)行網(wǎng)絡(luò)請(qǐng)求厂置,使用此庫(kù)Observable菩掏。
依賴庫(kù):
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
代碼邏輯:注意需要在自線程執(zhí)行,否則線程異常:android.os.NetworkOnMainThreadException
interface ApiService {
@GET("getUserData")
fun getUserData():Observable<UserBean>
}
fun netRequest() {
Thread(object :Runnable{
override fun run() {
// 網(wǎng)絡(luò)請(qǐng)求地址格式要寫對(duì)昵济,否則crash
// 需要網(wǎng)絡(luò)權(quán)限否則crash
val retrofit = Retrofit.Builder()
.baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
val service = retrofit.create(ApiService::class.java)
val call = service.getUserData()
// 需要自線程執(zhí)行否則報(bào):accept(t: Throwable?)--》android.os.NetworkOnMainThreadException
call.subscribe(object : Consumer<UserBean> {
override fun accept(userBean: UserBean?) {
Log.d(TAG, "accept-userBean: $userBean")
}
}, object : Consumer<Throwable> {
override fun accept(t: Throwable?) {
Log.d(TAG, "accept-error: $t")
}
})
}
}).start()
}
思路
不論是Call還是Observable類型智绸,只需要添加不同的CallAdapterFactory即可野揪,LiveData類型也是可以的。不管需要ResponseBody還是Bean對(duì)象類型瞧栗,也是添加不同的ConverterFactory即可斯稳,API返回XML格式也是可以解析的。
Retrofit.create()
Proxy.newProxyInstance實(shí)現(xiàn)動(dòng)態(tài)代理模式迹恐。通過動(dòng)態(tài)代理挣惰,將ApiService調(diào)用操作轉(zhuǎn)發(fā)給InvocationHandler來完成。Retrofit后續(xù)會(huì)通過反射拿到我們聲明的getUserData()時(shí)標(biāo)注各個(gè)配置項(xiàng)殴边,如:API路徑憎茂、請(qǐng)求方式、請(qǐng)求參數(shù)锤岸、返回值類型等信息竖幔,然后拼接為OkHttp的一個(gè)原始網(wǎng)絡(luò)請(qǐng)求。當(dāng)我們調(diào)用了call.enqueue時(shí)是偷,這個(gè)操作會(huì)觸發(fā)InvocationHandler去發(fā)起OkHttp網(wǎng)絡(luò)請(qǐng)求赏枚。
Retrofit會(huì)根據(jù)method是否是默認(rèn)方法來決定如何調(diào)用,loadServiceMethod(method)方法:
將每個(gè)代表接口方法的method轉(zhuǎn)化為ServiceMethod對(duì)象晓猛,包含了API的具體信息
因?yàn)锳PI可能會(huì)被多次調(diào)用饿幅,將構(gòu)造出來的ServiceMethod對(duì)象緩存到se rviceMethodCache中實(shí)現(xiàn)復(fù)用。
ServiceMethod
loadServiceMethod(method)返回的是一個(gè)ServiceMethod對(duì)象戒职,每個(gè)ServiceMethod對(duì)象對(duì)應(yīng)一個(gè)API接口方法栗恩,內(nèi)部包含對(duì)API解析結(jié)果。loadServiceMethod(method).invoke(args)調(diào)用API方法并傳遞參數(shù)的過程洪燥,對(duì)應(yīng):val call = service.getUserData()這個(gè)過程磕秤。
ServiceMethod是一個(gè)抽象類,包含抽象的invoke(Object[] args)方法捧韵。
ServiceMethod使用了工廠模式市咆,由于API的最終請(qǐng)求方式可能是多樣化的≡倮矗可能通過線程池來執(zhí)行蒙兰,也可以通過kotlin協(xié)程來執(zhí)行,使用工廠模式的意義可以將這種差異都隱藏在不同的ServiceMethod實(shí)現(xiàn)類中芒篷,外部統(tǒng)一通過parseAnnotations方法來獲取ServiceMethod實(shí)現(xiàn)類搜变。
parseAnnotations方法返回的ServiceMethod實(shí)際上是HttpServiceMethod,通過HttpServiceMethod.parseAnnotations返回的HttpServiceMethod實(shí)現(xiàn)针炉。
HttpServiceMethod
是ServiceMethod直接唯一字類挠他。HttpServiceMethod也是一個(gè)抽象類,包含兩個(gè)泛型聲明篡帕,ResponseT表示API方法返回值的外層包裝類型殖侵,ReturnT是實(shí)際需要的數(shù)據(jù)類型贸呢。例如fun getUserData():Call<UserBean>方法,ResponseT對(duì)應(yīng)的是Call拢军,ReturnT對(duì)應(yīng)的是UserBean楞陷。此外,HttpServiceMethod也實(shí)現(xiàn)了父類invoke方法朴沿,并轉(zhuǎn)交給另一個(gè)抽象方法adapt來完成猜谚,API網(wǎng)絡(luò)請(qǐng)求具體看adapt實(shí)現(xiàn)败砂。
Retrofit目前已經(jīng)支持Kotlin協(xié)程方式進(jìn)行調(diào)用了赌渣。
parseAnnotations主要邏輯:
1、先通過createCallAdapter(retrofit, method, adapterType, annotations)方法拿到CallAdapter對(duì)象昌犹,實(shí)現(xiàn)API方法的不同返回值包裝類處理邏輯坚芜。例如:getUserData()方法返回的值包裝類類型Call,那返回CallAdapter對(duì)象對(duì)應(yīng)DefaultCallAdapterFactory包含的Adapter斜姥;如果是Observable,那么返回的就是RxJava2CallAdapterFactory包含的Adapter鸿竖。
2、通過createResponseConverter(retrofit, method, responseType)方法拿到Converter對(duì)象铸敏,Converter就用于實(shí)現(xiàn)API方法的不同返回值處理邏輯缚忧。例如:getUserData()返回類型ResponseBody,那么Converter對(duì)象就對(duì)應(yīng)BuiltInConverters杈笔;如果是UserBean,那么對(duì)應(yīng)GsonConverterFactory
根據(jù)上面兩步闪水,構(gòu)造出一個(gè)CallAdapted對(duì)象并返回。
CallAdapter是HttpServiceMethod的子類蒙具,在InvocationHandler中通過loadServiceMethod(method).invoke(args)發(fā)起調(diào)用鏈球榆,會(huì)先創(chuàng)建出一個(gè)OkHttpCall對(duì)象,并最后調(diào)用callAdapter.adapt(call)方法
OkHttpCall
是實(shí)際發(fā)起OkHttp請(qǐng)求的地方禁筏。當(dāng)調(diào)用getUserData():Call<ResponseBody>方法時(shí)持钉,返回的Call對(duì)象實(shí)際上是OkHttpCall類型,而當(dāng)我們調(diào)用call.enqueue(callback)方法時(shí)篱昔,enqueue方法會(huì)發(fā)起一個(gè)OkHttp請(qǐng)求每强,傳入的Callback對(duì)象會(huì)由okhttp3.callback本身回調(diào)進(jìn)行中轉(zhuǎn)調(diào)用。
總結(jié)
通過retrofit.create(ApiService::class.java)得到ApiService動(dòng)態(tài)實(shí)現(xiàn)類州刽,通過Java原生提供的Proxy.newProxyInstance代表的動(dòng)態(tài)代理功能來實(shí)現(xiàn)的舀射。拿到ApiService實(shí)現(xiàn)類,可以直接調(diào)用ApiService中聲明的方法怀伦。
當(dāng)我們調(diào)用了service.getUserData()方法脆烟,Retrofit會(huì)將每個(gè)API方法都抽象封裝為一個(gè)ServiceMethod并緩存起來,操作會(huì)轉(zhuǎn)交給ServiceMethod來完成房待,由ServiceMethod來負(fù)責(zé)返回我們的目標(biāo)類型邢羔,對(duì)應(yīng)的是ServiceMethod.invoke(Object[] args)方法驼抹,args代表的是我們調(diào)用的API方法時(shí)需要傳遞參數(shù),對(duì)應(yīng)本例是空數(shù)組拜鹤。
ServiceMethod只具有唯一的子類HttpServiceMethod, 而HttpServiceMethod會(huì)invoke方法構(gòu)建出一個(gè)OkHttpCall對(duì)象框冀,然后調(diào)用其抽象方法adapt
對(duì)于不同的請(qǐng)求方式,ServiceMethod.parseAnnotations方法最終會(huì)返回不同的HttpServiceMethod子類敏簿。本例明也,最終會(huì)返回CallAdapter對(duì)象
Kotlin 協(xié)程方式來調(diào)用
依賴庫(kù):
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
}
通過runBlocking來啟動(dòng)一個(gè)協(xié)程,避免請(qǐng)求還未結(jié)束main線程就停止的情況惯裕。實(shí)際開發(fā)中要避免runBlocking這樣使用協(xié)程温数。
interface ApiService {
@GET("getUserData")
suspend fun getUserData():UserBean
}
fun test2() {
val retrofit = Retrofit.Builder().baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(ApiService::class.java)
runBlocking {
Log.d(TAG, "test2: isMain-1-:${Looper.myLooper() == Looper.getMainLooper()}")
val job: Job = launch {
Log.d(TAG, "test2: isMain-2-:${Looper.myLooper() == Looper.getMainLooper()}")
try {
val userBean = service.getUserData()
Log.d(TAG, "test2: userBean:$userBean")
} catch (e: Throwable) {
Log.d(TAG, "test2: onFailure:$e")
}
}
}
}
getUserData()不需要任何包裝類了,直接聲明目標(biāo)數(shù)據(jù)類型就可以蜻势,使用更簡(jiǎn)潔撑刺。
每個(gè)方法用suspend關(guān)鍵字進(jìn)行修飾,標(biāo)明其只能在協(xié)程中調(diào)用握玛。
Retrofit是以Java語言實(shí)現(xiàn)的够傍,但suspend關(guān)鍵字只能用于Kotlin,兩者存在“溝通障礙”,但只要調(diào)用方也屬于JVM語言挠铲,那Retrofit就是可以使用的冕屯,通過IDEA將ApiService反編譯為Java類,看下suspend函數(shù)在Retrofit的角度來看是怎樣實(shí)現(xiàn)的
public interface ApiService {
@GET("getUserData")
@Nullable
Object getUserData1(@NotNull Continuation var1);
方法返回值類型變?yōu)镺bject拂苹,方法參數(shù)列表中添加了一個(gè)kotlin.coroutines.Continuation參數(shù)安聘。
在RequestFactory類中包含了一個(gè)isKotlinSuspendFunction的boolen類型的變量,當(dāng)前解析的Method是否是suspend函數(shù)醋寝。RequestFactory的build()方法中搞挣,對(duì)API方法每個(gè)參數(shù)進(jìn)行解析,包含了檢測(cè)當(dāng)前解析參數(shù)是否屬于最后一個(gè)參數(shù)的邏輯音羞。
如果檢測(cè)到最后一個(gè)參數(shù)類型是Continuation.class囱桨,那isKotlinSuspendFunction就會(huì)變?yōu)閠rue。
API最后一個(gè)參數(shù)強(qiáng)轉(zhuǎn)為Continuation<ResponseT>類型嗅绰,調(diào)用KotlinExtensions.await(call,continuation)這個(gè)Kotlin的擴(kuò)展函數(shù)
await()以suspendCancellableCoroutine這個(gè)支持cancel的CoroutineScope作為作用域舍肠,以Call.enqueue的方式發(fā)起OkHttp請(qǐng)求,拿到responseBody后就透?jìng)鞒鰜砭矫妫瓿烧麄€(gè)調(diào)用流程翠语。
Retrofit對(duì)Android平臺(tái)
Retrofit并不需要依賴于Android平臺(tái),可以用于任意的Java客戶端财边,Retrofit只是對(duì)Android平臺(tái)進(jìn)行了特殊實(shí)現(xiàn)肌括。
在構(gòu)建Retrofit對(duì)象時(shí)候,可以選擇傳遞一個(gè)Platform對(duì)象用于標(biāo)記調(diào)用方所處的平臺(tái)
1酣难、判斷是否支持Java8谍夭,是否支持調(diào)用interface的默認(rèn)方法黑滴,判斷是否支持Optional和CompletableFuture要用到。因?yàn)锳ndroid應(yīng)用如果支持Java8,需要Gradle文件進(jìn)行主動(dòng)配置紧索,Java8在Android平臺(tái)目前也支持不徹底袁辈。
2、實(shí)現(xiàn)main線程回調(diào)的Executor珠漂。Android平臺(tái)不允許在main線程上執(zhí)行耗時(shí)任務(wù)晚缩,UI操作都需要切換到main線程來完成。對(duì)于Android平臺(tái)媳危,Retroft回調(diào)網(wǎng)絡(luò)請(qǐng)求結(jié)果荞彼,通過main線程執(zhí)行的Executor來進(jìn)行線程切換。