一、添加依賴html
implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
複製代碼
備註:本文使用的是2.6.2版本java
二、如何使用android
2.一、建立接口文件:WanAndroidApigit
interface WanAndroidApi {
@GET("banner/json")
fun getBannerData(): Call<ResponseBody>
}
複製代碼
2.二、構造Retrofit對象併發起一個簡單的請求github
val baseUrl = "https://www.wanandroid.com/"
val retrofit = Retrofit.Builder().baseUrl(baseUrl).build()
val wanAndroidApi = retrofit.create(WanAndroidApi::class.java)
wanAndroidApi.getBannerData().enqueue(object : Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
println("onFailure: ${t.message}")
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
println("onResponse: ${response.body()?.string()}")
}
})
複製代碼
2.三、請求打印結果json
System.out: onResponse: {"data":[{"desc":"Android高級進階直播課免費學習","id":23,"imagePath":"https://wanandroid.com/blogimgs/67c28e8c-2716-4b78-95d3-22cbde65d924.jpeg","isVisible":1,"order":0,"title":"Android高級進階直播課免費學習","type":0,"url":"https://url.163.com/4bj"},{"desc":"一塊兒來作個App吧","id":10,"imagePath":"https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png","isVisible":1,"order":0,"title":"一塊兒來作個App吧","type":1,"url":"https://www.wanandroid.com/blog/show/2"},{"desc":"","id":6,"imagePath":"https://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png","isVisible":1,"order":1,"title":"咱們新增了一個經常使用導航Tab~","type":1,"url":"https://www.wanandroid.com/navi"},{"desc":"","id":20,"imagePath":"https://www.wanandroid.com/blogimgs/90c6cc12-742e-4c9f-b318-b912f163b8d0.png","isVisible":1,"order":2,"title":"flutter 中文社區 ","type":1,"url":"https://flutter.cn/"}],"errorCode":0,"errorMsg":""}
複製代碼
單純的Retrofit用法就是這麼簡單,幾行代碼實現一個網絡請求,固然配合RxJava一塊兒使用是近幾年來最主流的網絡架構,本文是解讀Retrofit源碼就不對RxJava用法作過多說明了。設計模式
閱讀任何源碼都要帶着疑問去閱讀,經過閱讀去尋找答案,就能更深入的理解源碼。api
筆者在使用的時候心中就有兩個疑問:緩存
接下來就讓咱們帶着疑問去從源碼中尋找答案吧。bash
一、咱們通常都是先建立一個接口類:WanAndroidApi
備註:這個接口裏邊是各類註解,關於註解的用法請移步什麼是註解
interface WanAndroidApi {
@GET("banner/json")
fun getBannerData(): Call<ResponseBody>
}
複製代碼
二、而後經過retrofit.create(WanAndroidApi::class.java)
返回一個WanAndroidApi
對象,而後調用這個對象的getBannerData()
方法進行網絡請求。
看到此處就會心有疑問,咱們知道接口是不能直接建立對象的,爲何經過
retrofit.create()
以後就能建立WanAndroidApi
對象吶,接下來咱們看下create方法的源碼
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//看到Proxy彷佛有點明白,是經過動態代理去處理的
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//重點1 //重點2
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
複製代碼
retrofit核心就是使用動態代理建立接口代理對象進行處理的,關於動態代理的用法請移步Java 動態代理詳解和10分鐘看懂動態代理設計模式
loadServiceMethod(method)
ServiceMethod<?> loadServiceMethod(Method method) {
//首先從緩存中獲取ServiceMethod
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
//沒有緩存過的話再去建立並存入到緩存
if (result == null) {
//重點3
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
複製代碼
經過緩存讀取,沒有緩存的話經過
ServiceMethod.parseAnnotations
建立出ServiceMethod對象,而後緩存起來,方便下次使用
ServiceMethod.parseAnnotations(this, method)
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//重點4: 經過RequestFactory.parseAnnotations解析註解中請求方法和參數信息封裝到RequestFactory中
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
//重點5:把RequestFactory傳給HttpServiceMethod進一步解析
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
複製代碼
經過RequestFactory.parseAnnotations(retrofit, method)解析註解中請求方法和參數信息封裝到RequestFactory中
HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
Annotation[] annotations = method.getAnnotations();
······
//Retrofit的CallAdapter
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
······
Retrofit的ConverterAdapter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
//重點6
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
······
}
複製代碼
CallAdapted
CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
//裏邊這是各類參數的賦值,看super方法便可,把上邊全部解析出來的參數賦值到對應的對象
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
複製代碼
至此這條分析路線1->3->4->5->6邏輯所有跟蹤完畢,此時回到回到文中標記的重點1處,爲了方便查看直接在下邊貼出對應代碼
//重點1 //重點2
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
複製代碼
經過以上分析咱們知道loadServiceMethod
方法就是解析各類註解參數設置給相應對象,而後調用invoke
方法,咱們再來看看invoke
方法作了什麼
invoke
@Override final @Nullable ReturnT invoke(Object[] args) {
//重點7
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
複製代碼
invoke
方法建立了OkHttpCall
對象,咱們看看OkHttpCall
是什麼東東
final class OkHttpCall<T> implements Call<T> {
@Override public synchronized Request request() {
okhttp3.Call call = rawCall;
try {
return (rawCall = createRawCall()).request();
}
}
@Override public void enqueue(final Callback<T> callback) {
...
call = rawCall = createRawCall();
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
}
callback.onResponse(OkHttpCall.this, response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
callback.onFailure(OkHttpCall.this, e);
}
});
}
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
...
call = rawCall = createRawCall();
return parseResponse(call.execute());
}
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
return call;
}
}
複製代碼
OkHttpCall
實現了Call
接口方法,在裏邊是經過okhttp
進行網絡請求,並把請求結果經過callback
回調出去,至此整個流程已經所有分析完畢
經過上邊對源碼的分析咱們能夠總結retrofit的幾個關鍵點:
接下來就咱們就仿照retrofit的實現方式手擼一個簡易版的retrofit試試
參照Retrofit
咱們須要定一個請求的註解方法 以get
方法爲例
@kotlin.annotation.Target(AnnotationTarget.FUNCTION)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class GET(
val value: String = ""
)
複製代碼
有了GET請求註解咱們就能夠定義咱們的接口api了,細心的你會發下接口裏邊須要一個返回值Call
,那咱們就定義一個Call
,代碼以下
//參照Rerofit的Call,咱們只寫一個方法enqueue便可
interface Call<T> {
fun enqueue(callBack: CallBack<T>)
}
複製代碼
這裏又用到了CallBack
,那繼續寫一個CallBack
接口
interface CallBack<T> {
//請求成功回調
fun onResponse(response: Response?)
//請求失敗回調
fun onFailure(t: Throwable)
}
複製代碼
有了以上定義的方法咱們就能夠寫ApiService
了
interface MyApiService {
@GET("banner/json")
fun getBannerData():Call<ResponseBody>
}
複製代碼
定義好
ApiService
接口以後就要建立一個咱們本身的retrofit
了
建立MyRetrofit類
class MyRetrofit(private val baseUrl: String?) {
//也使用Builder模式構建retrofit對象
class Builder {
var baseUrl: String? = null
fun setBaseUrl(baseUrl: String): Builder {
this.baseUrl = baseUrl
return this
}
fun build(): MyRetrofit {
return MyRetrofit(baseUrl)
}
}
//參照retrofit的源碼使用Proxy.newProxyInstance動態代理處理
fun <T> create(service: Class<T>): T {
return Proxy.newProxyInstance(
service.classLoader,
arrayOf(service),
InvocationHandler { any, method, arrayOfAnys ->
var call: Call<T>? = null
val annotations = method.declaredAnnotations
for (annotation in annotations) {
call = parseHttpMethodAndPath(annotation)
}
return@InvocationHandler call
}
) as T
}
private fun <T> parseHttpMethodAndPath(annotation: Annotation): Call<T>? {
var call: Call<T>? = null
if (annotation is GET) {
val path = annotation.value
val url = "$baseUrl$path"
println("url= $url")
val okHttpClient: OkHttpClient = OkHttpClient().newBuilder().build()
val request: Request = Request.Builder().url(url).build()
call = object : Call<T> {
override fun enqueue(callBack: CallBack<T>) {
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
callBack.onFailure(e)
}
override fun onResponse(call: okhttp3.Call, response: Response) {
callBack.onResponse(response)
}
})
}
}
}
return call
}
}
複製代碼
在判斷是否是
GET
請求,是的話解析註解path和參數等信息,拼接一個完整的url
,而後構建出okhttp
對象進行網絡請求,把結果回調給callback
至此簡易版的retrofit已經寫完了,接下來咱們就驗證一下是否能請求成功
val myRetrofit = MyRetrofit.Builder().setBaseUrl(baseUrl).build()
val apiService = myRetrofit.create(MyApiService::class.java)
apiService.getBannerData().enqueue(object : CallBack<ResponseBody> {
override fun onResponse(response: okhttp3.Response?) {
println("MyRetrofit---->onResponse: ${response?.body()?.string()}")
}
override fun onFailure(t: Throwable) {
println("MyRetrofit---->onFailure: ${t.message}")
}
})
複製代碼
請求結果以下:
System.out: MyRetrofit---->onResponse: {"data":[{"desc":"Android高級進階直播課免費學習","id":23,"imagePath":"https://wanandroid.com/blogimgs/67c28e8c-2716-4b78-95d3-22cbde65d924.jpeg","isVisible":1,"order":0,"title":"Android高級進階直播課免費學習","type":0,"url":"https://url.163.com/4bj"},{"desc":"一塊兒來作個App吧","id":10,"imagePath":"https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png","isVisible":1,"order":0,"title":"一塊兒來作個App吧","type":1,"url":"https://www.wanandroid.com/blog/show/2"},{"desc":"","id":6,"imagePath":"https://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png","isVisible":1,"order":1,"title":"咱們新增了一個經常使用導航Tab~","type":1,"url":"https://www.wanandroid.com/navi"},{"desc":"","id":20,"imagePath":"https://www.wanandroid.com/blogimgs/90c6cc12-742e-4c9f-b318-b912f163b8d0.png","isVisible":1,"order":2,"title":"flutter 中文社區 ","type":1,"url":"https://flutter.cn/"}],"errorCode":0,"errorMsg":""}
複製代碼
結果符合咱們的預期,證實咱們前面的分析是正確的!
能夠看看咱們只建立了幾個類寫了幾行代碼就能夠像使用
Retrofit
同樣的用法進行網絡請求了,固然示例代碼只是實現了一個最基礎的,畢竟源碼仍是很複雜的,他們考慮了擴展性,易用性等,裏邊各類封裝各類設計模式隨處可見,是一個很是值得學習的庫,感謝Retrofit
團隊的付出。無論他封裝的再複雜,萬變不離其宗,咱們掌握了它的實現原理之後再項目中遇到問題就能作到心中有數,作到知己知彼,方能解出bug。