Android Retrofit框架分析(三):自动切换回主线程;bulid的过程;create方法+ServiceMethod源码了解
目录
- 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代码的时候,也会有一种恍然大悟的感觉,比如回调的这个地方,已经自动会切换到主线程。