【Android】Retrofit源码学习

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:

【Android】Retrofit源码学习
使用Retrofit的流程#
通过Builder创建Retrofit对象:
Copy
Retrofit retrofit = new Retrofit.Builder().baseUrl("").addConverterFactory().build();
用Java注解描述API
Copy
public interface MyApi {

@GET("/api")
Call<Data> getData();

}
通过retrofit创建api对象,并建立Call对象
Copy
MyApi api = retrofit.create(MyApi.class);
Call call = api.getData();
通过Call对象获取数据,enqueue()方法发送异步请求,同步方式则使用execute()方法
Copy
call.enqueue(new Callback() {

@Override
public void onResponse(Response<ZhuanLanAuthor> author) {
    System.out.println("name: " + author.getName());
}
@Override
public void onFailure(Throwable t) {
}

});
原理解析#
Retrofit所做的事情:将Java接口翻译成HTTP请求,然后用OkHttp去发送请求。

Retrofit使用动态代理实现了这件事

动态代理#
动态代理可以在不实现相同接口的proxy的情况下,对相关方法进行代理。

Java可以通过Proxy类实现代理模式,而其中的newProxyInstance()方法可以实现动态代理。通过实现InvocationHandler接口来定义代理动作。

Proxy.newProxyInstance(ClassLoader, Class<?>[] interfaces,InvocationHandler)
InvocationHandler的接口定义如下:

Copy
public interface InvocationHandler {

public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

}
参数含义:

proxy:代理对象
method: 代理方法
args: 方法的参数
实现invoke()方法来进行代理:

Copy
public Object invoke(Object proxy, Method method, Object[] args)

    throws Throwable {
// do something
method.invoke(target, args);
// do something

}
这样便能够成功对target方法进行代理,动态代理生成的代理类的名字为包名+$Proxy+id序号

请求流程分析#
回到使用方法,一开始要使用create()生成API的对象

Copy
MyApi api = retrofit.create(MyApi.class);
这里看下create()的源码:

Copy
public T create(final Class service) {

validateServiceInterface(service); // 判断是否为接口
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 {
        // 如果是Object的方法则直接调用
        if (method.getDeclaringClass() == Object.class) {
          return method.invoke(this, args);
        }
        // 兼容Java8,Android平台不会调用, 确认平台的方法是通过反射机制判断类的加载信息
        if (platform.isDefaultMethod(method)) {
          return platform.invokeDefaultMethod(method, service, proxy, args);
        }
        // 主要方法,返回ServiceMethod对象
        return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
      }
    });

}
create使用了动态代理的方法,返回了Proxy.newProxyInstance()动态代理对象

所以api对象为动态代理对象,不是真正的实现接口产生的对象。当api对象调用getData()方法时会被动态代理拦截,然后调用InvocationHandler对象中的invoke()方法。

然后Retrofit通过反射获取到getData()方法的注解信息,配合invoke()的args参数,创建一个ServiceMethod对象

ServiceMethod传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api的域名、path、http请求方法、请求头、body等等。最后返回一个Call对象。

Copy
ServiceMethod<?> loadServiceMethod(Method method) {

ServiceMethod<?> result = serviceMethodCache.get(method); // 如果有缓存则直接用
if (result != null) return result;

synchronized (serviceMethodCache) { 
    result = serviceMethodCache.get(method); // 线程安全,锁住后查看其他线程是否有加载
    if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method); // 解析注解
        serviceMethodCache.put(method, result); // 放入Cache中
    }
}
return result;

}
跟进ServiceMethod.parseAnnotation():

Copy
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {

// 解析注解为HTTP请求的相关信息
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) { //API接口方法返回值不能为void
    throw methodError(method, "Service methods cannot return void.");
}

return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);

}
这里构建了一个RequestFactory对象,解析了接口中关于Http协议的相关信息,具体解析方法就是拿到Method的Annotation后instansof比较确定

Copy
RequestFactory(Builder builder) {

method = builder.method;
baseUrl = builder.retrofit.baseUrl;
httpMethod = builder.httpMethod;
relativeUrl = builder.relativeUrl;
headers = builder.headers;
contentType = builder.contentType;
hasBody = builder.hasBody;
isFormEncoded = builder.isFormEncoded;
isMultipart = builder.isMultipart;
parameterHandlers = builder.parameterHandlers;
isKotlinSuspendFunction = builder.isKotlinSuspendFunction;

}
解析完后使用HttpServiceMethod.parseAnnotations()最后生成HttpServiceMethod对象

Copy
static HttpServiceMethod parseAnnotations(

Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; // kotlin支持,先忽略
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;

Annotation[] annotations = method.getAnnotations(); // 方法的注解 @GET @POST @DELETE等
Type adapterType;
// ...这里有一段关于Kotlin支持的代码,adapterType
adapterType = method.getGenericReturnType(); // 接口方法的返回类型,一般为Call<T>

CallAdapter<ResponseT, ReturnT> callAdapter =
    createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
    throw methodError(method, "'"
                      + getRawType(responseType).getName()
                      + "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
    throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// HEAD请求没有Response Body
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
    throw methodError(method, "HEAD method must use Void as response type.");
}
// 设置Response的解析,可以是json解析
Converter<ResponseBody, ResponseT> responseConverter =
    createResponseConverter(retrofit, method, responseType);

okhttp3.Call.Factory callFactory = retrofit.callFactory; // 这里若是没有自定则默认为OkHttpClient
if (!isKotlinSuspendFunction) {
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); // 不使用Kotlin就关注这里就行了
}
// ...关于Kotlin的return

}
最后返回了HttpServiceMethod的继承类CallAdapted,其中存放着RequestFactory、Converter、CallFactory

然后我们返回来看这段代码

Copy
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
这里调用的invoke方法来为HttpServiceMethod中的invoke方法:

Copy
@Override final @Nullable ReturnT invoke(Object[] args) {

Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);

}
// CallAdapted中
@Override protected ReturnT adapt(Call call, Object[] args) {

return callAdapter.adapt(call);

}
这里的OKHttpCall为Okhttp3.Call的封装类,并实现了Call的相关方法enqueue、execute。

这里最后使用的adapt方法调用了Retrofit对象中的callAdapter.adapt()来对Call对象进行了适配。

若是开始初始化Retrofit对象时没有设置CallAdapter,则回默认使用Call,api接口定义时方法的返回类型只能是Call

所以便能解释如下代码:

Copy
Call call = api.getData();
api对象为一个动态代理对象,当执行getData()时进入动态代理函数,在InvocationHandler的invoke函数最后调用了HttpServiceMethod.invoke(args),返回了一个Call对象。

响应流程分析#
Retrofit使用中最后调用自定义的API接口方法返回了Call对象,这个对象实际上是Retrofit自己封装的OkHttpCall对象,随后我们使用enqueue方法发出异步请求

Copy
call.enqueue(new CallBack() {

@Override
public void onResponse(Call<MyData> call, Response<MyData> response) {
    //... on response    
}
@Override
public void onFailure(Call<MyData> call, Throwable t) {
    //... on response    
}

})
跟进OkHttpCall.enqueue的源码:

Copy
@Override public void enqueue(final Callback callback) {

Objects.requireNonNull(callback, "callback == null"); // callback不能为null

okhttp3.Call call; // okhttp3的Call对象
Throwable failure;

synchronized (this) { // 线程安全
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    call = rawCall; // rawCall为OkHttpCall保存的Okttp3的Call对象
    failure = creationFailure;
    if (call == null && failure == null) {
        try {
            // createRawCall中使用callFactory.newCall(requestFactory.create(args))
            // 实际上就是OkHttpClient.newCall(OkHttp3.Request)
            // 返回了OkHttp3.Call对象
            call = rawCall = createRawCall();
        } catch (Throwable t) {
            throwIfFatal(t);
            failure = creationFailure = t;
        }
    }
}

if (failure != null) {
    callback.onFailure(this, failure);
    return;
}

if (canceled) {
    call.cancel();
}
// 使用okhttp3.Call的enqueue方法
call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)           {
        Response<T> response;
        try {
            // 这里使用了Converter来解析Response
            // 将Okhttp3.Response对象解析成Retrofit封装的Response对象
            response = parseResponse(rawResponse);
        } catch (Throwable e) {
            throwIfFatal(e);
            callFailure(e);
            return;
        }

        try {
            // 调用传进来的回调
            callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace();
        }
    }

    @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
    }
    // 请求失败则进入callback的OnFailure方法
    private void callFailure(Throwable e) {
        try {
            callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace();
        }
    }
});

}
其中parseResponse()方法:

Copy
Response parseResponse(okhttp3.Response rawResponse) throws IOException {

ResponseBody rawBody = rawResponse.body();

// 将Response Body 和 ResponseHeader 分开
// 之后再对Body进行处理
rawResponse = rawResponse.newBuilder()
    .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
    .build();

int code = rawResponse.code(); // HTTP 状态码
// 响应不成功
if (code < 200 || code >= 300) {
    try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
    } finally {
        rawBody.close();
    }
}
// 响应无内容,填入null
if (code == 204 || code == 205) {
    rawBody.close();
    return Response.success(null, rawResponse);
}
// 保存source的Response Body,在解析失败时可以使用
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
    // 使用responseConverter来解析Body
    T body = responseConverter.convert(catchingBody);
    // 将解析好的Body装入Retrofit的Response对象返回
    return Response.success(body, rawResponse);
} catch (RuntimeException e) {
    catchingBody.throwIfCaught();
    throw e;
}

}
主要的parse过程便是,将Okhttp.Response对象的Body和Header拆开,若请求成功且Body有内容则将Body交给responseConverter取解析成响应对象,装入Retrofit的Response对象中返回。

总结#
Retrofit的特色:通过使用注解定义API接口的方式声明API,通过注解的解析,将解析得到的信息封装在RequestFactory中,在使用时调用create()方法生成Okhttp的Request对象。
通过动态代理的方式,代理用户定义的API接口方法,使其生成封装的OkHttpCall对象
封装okhttp.Call为OkHttpCall,使其能够使用CallAdapter(可以使返回的Call对象适配为其他的对象,如RxJava(没用过)中的对象)和ResponseConverter(支持Gson等解析)
目前只读到这里,还有一些机制没读完
参考文章#
https://www.jianshu.com/p/c1a3a881a144

https://segmentfault.com/a/1190000006767113

https://yq.aliyun.com/articles/658544

作者: y4ngyy

出处:https://www.cnblogs.com/y4ngyy/p/12530566.html

相关文章
|
2月前
|
Ubuntu 开发工具 Android开发
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
117 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
|
5天前
|
Java Maven 开发工具
第一个安卓项目 | 中国象棋demo学习
本文是作者关于其第一个安卓项目——中国象棋demo的学习记录,展示了demo的运行结果、爬坑记录以及参考资料,包括解决Android Studio和maven相关问题的方法。
第一个安卓项目 | 中国象棋demo学习
|
1月前
|
Android开发
Android学习 —— 测试init.rc中的条件触发的处理顺序
Android学习 —— 测试init.rc中的条件触发的处理顺序
|
2月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
56 1
|
2月前
|
Android开发 Docker 容器
docker中编译android aosp源码,出现Build sandboxing disabled due to nsjail error
在使用Docker编译Android AOSP源码时,如果遇到"Build sandboxing disabled due to nsjail error"的错误,可以通过在docker run命令中添加`--privileged`参数来解决权限不足的问题。
172 1
|
2月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
168 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
2月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
48 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
2月前
|
Android开发
我的Android 进阶修炼(1): AOSP源码根目录结构
本文介绍了AOSP源码的根目录结构,提供了基于MTK9269 Android 9.0源码的目录说明,帮助读者了解AOSP源码的组织方式和各目录的功能。
63 0
我的Android 进阶修炼(1): AOSP源码根目录结构
|
2月前
|
搜索推荐 Android开发
学习AOSP安卓系统源代码,需要什么样的电脑?不同配置的电脑,其编译时间有多大差距?
本文分享了不同价位电脑配置对于编译AOSP安卓系统源代码的影响,提供了从6000元到更高价位的电脑配置实例,并比较了它们的编译时间,以供学习AOSP源代码时电脑配置选择的参考。
84 0
学习AOSP安卓系统源代码,需要什么样的电脑?不同配置的电脑,其编译时间有多大差距?
|
2月前
|
API 开发工具 Android开发
Android源码下载
Android源码下载
153 0
下一篇
无影云桌面