目錄
- Okhttp有什么不好?
- bulid的過程
- create方法+ServiceMethod
- call + enqueue的過程
- 為什么要學習源碼呢?
一、Okhttp有什么不好?
Okhttp本身來說,是一個挺好的網絡框架,但,對于開發者而言,使用起來,會過于繁瑣。下面我們看看一個代碼:
// 1?? 手動拼接URL和參數(容易出錯)
HttpUrl url = HttpUrl.parse("https://api.example.com/user").newBuilder().addQueryParameter("id", "123").build();// 2?? 創建請求對象
Request request = new Request.Builder().url(url).build();// 3?? 發起異步請求
OkHttpClient client = new OkHttpClient();
client.newCall(request).enqueue(new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {// 4?? 手動解析JSON(容易遺漏判空)String json = response.body().string();User user = new Gson().fromJson(json, User.class);// 5?? 手動切回主線程更新UI(忘記切換會崩潰)runOnUiThread(() -> {textView.setText(user.getName());});}@Overridepublic void onFailure(Call call, IOException e) {// 6?? 手動處理失敗邏輯(比如Toast錯誤)}
});
?問題總結??:
- ??代碼臃腫??:每個請求都要寫重復代碼(拼參數、解析JSON、線程切換)。
- ??維護困難??:如果接口路徑或參數變更,需要全局搜索修改。
- ??容錯成本高??:手動處理空指針、JSON解析異常、線程安全問題。
下面我們看看Retrofit的代碼:
// 1?? 聲明接口(像寫文檔一樣直觀)
public interface ApiService {@GET("user")Call<User> getUser(@Query("id") String id); // 自動拼接參數
}// 2?? 發起請求(3行代碼搞定)
ApiService service = retrofit.create(ApiService.class);
service.getUser("123").enqueue(new Callback<User>() {@Overridepublic void onResponse(Call<User> call, Response<User> response) {// ? 自動解析JSON → User對象// ? 自動切回主線程textView.setText(response.body().getName());}
});
優化原理??:
- ??聲明式API??:用注解代替手動拼參數(如
@GET
定義接口路徑,@Query
自動拼接URL參數)。 - ??自動解析??:通過
GsonConverter
直接將JSON轉成User
對象。 - ??線程安全??:回調時自動切換回主線程(背后是
MainThreadExecutor
)。
那么他是如何做到的呢?接下來,我們看看源碼。
二、bulid的過程
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.example.com/") // 必填:API根路徑.client(new OkHttpClient()) // 選填:自定義OkHttp(比如加日志攔截器).addConverterFactory(GsonConverterFactory.create()) // 選填:數據解析器.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 選填:適配RxJava.build();
這個主要是做什么?可以理解為就是將所需的參數全部保存起來,方便后面使用,比如url,數據解析器等,都是為后面發起請求,和接收響應進行使用,使用bulid構建一個Retrofit,這就是bulid的作用。我們可以進入他的源碼看看:
比如addConverterFactory以及addCallAdapterFactory方法,都是使用List來保存起來,
url也是
然后使用bulid構建一個Retrofit。
建造過程核心邏輯??:
- ??校驗必填參數??:比如
baseUrl
不能為空。 - ??設置默認組件??:如果沒配置
CallAdapter
,默認用ExecutorCallAdapterFactory
(處理主線程回調)。 - ??組合所有配置??:將
Converter
、CallAdapter
等組件打包到Retrofit對象中。
三、create方法+ServiceMethod
// 1?? 聲明接口(像寫文檔一樣直觀)
public interface ApiService {@GET("user")Call<User> getUser(@Query("id") String id); // 自動拼接參數
}// 2?? 發起請求(3行代碼搞定)
ApiService service = retrofit.create(ApiService.class);
service.getUser("123");
當我們調用retrofit.create方法的時候,我們看看內部做了什么。
通過 Proxy.newProxyInstance
創建接口的代理對象,那么當我們調用service.getUser(“123”)的時候,代理對象的所有方法調用都會路由到 InvocationHandler.invoke()
。讓所有的接口都走 invoke函數,這樣就可以攔截調用函數的執行,從而將網絡接口的參數配置歸一化。這個invoke方法,就是典型的AOP思想,在中間切開一個口。
invoke函數是如何完成網絡請求,從這個retrofit到okhttp呢?
接下來,我們看看loadServiceMethod方法
這個方法返回了一個ServiceMethod,這里面主要做了什么?解析方法上的 @GET
、@POST
等注解,解析 @Query
、@Path
、@Body
等參數注解,解析結果會被緩存到 serviceMethodCache
避免重復解析。
每個接口方法(如 getUser()
)首次調用時都會生成專屬的 ServiceMethod
,即使同一個接口中的不同方法(如 getUser()
和 login()
),也會生成不同的 ServiceMethod
,然后通過 serviceMethodCache
的 ConcurrentHashMap 緩存起來,Key 為 Method
對象。
四、call + enqueue的過程
那么接口方法上的所有信息,參數都已經拿到了,也解析好了,接下來干嘛?我們繼續看回源碼這里,會調用invoke方法。
創建 OkHttpCall 對象,通過傳遞進來的參數,json解析成對應的bean,返回封裝了okhttp的call
call有了以后,接下來就是調用enqueue方法
在得到response以后,要返回給主線程
底層還是handler。
五、為什么要學習源碼呢?
一開始的時候,在想,為什么要看源碼,看了源碼以后,你了解他的原理,你也可以運用他的技術,用到其他方面,比如注解的使用,動態代理的使用。
然后也能了解他的底層邏輯,后面我們寫retrofit代碼的時候,也會有一種恍然大悟的感覺,比如回調的這個地方,已經自動會切換到主線程。