学习目标:
- 搞明白Retrofit实现原理
- 手撸一个简易版的Retrofit
如何使用Retrofit
1、添加依赖
implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
备注:本文使用的是2.6.2版本
2、如何使用
2.1、创建接口文件:WanAndroidApi
interface WanAndroidApi {
@GET("banner/json")
fun getBannerData(): Call<ResponseBody>
}
2.2、构造Retrofit对象并发起一个简单的请求
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.3、请求打印结果
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用法做过多说明了。
源码分析
分析源码之前需要做些什么
阅读任何源码都要带着疑问去阅读,通过阅读去寻找答案,就能更深刻的理解源码。
笔者在使用的时候心中就有两个疑问:
- 1、为什么定义一个interface就能调用接口方法进行请求
- 2、Retrofit内部到底是如何工作的
接下来就让我们带着疑问去从源码中寻找答案吧。
从哪里开始分析源码
1、我们一般都是先创建一个接口类:WanAndroidApi
备注:这个接口里边是各种注解,关于注解的用法请移步什么是注解
interface WanAndroidApi {
@GET("banner/json")
fun getBannerData(): Call<ResponseBody>
}
2、然后通过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分钟看懂动态代理设计模式
源码解析---重点1---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对象,然后缓存起来,方便下次使用
源码解析---重点3---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中
源码解析---重点5---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);
}
······
}
源码解析---重点6---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
方法做了什么
源码解析---重点2---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的几个关键点:
- 通过注解方式把请求path、参数等信息标记在xxxApi接口中;
- retrofit.create里边的动态代理方法返回xxxApi的代理对象;
- 代理方法内部进行注解解析,并构建出okhttp对象方式请求,返回call对象;
- 调用xxxApi的接口方法返回的call对象通过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。