square/retrofit Github地址 最新版本从这里获取!
Retrofit 是一款由 Square 公司开发的用于 Android 和 Java 应用程序的网络请求库,旨在简化 HTTP 网络请求的过程,Retrofit 最初是在 2013 年开源发布的,已经风靡了10年。其优点如下
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// 下面三个是rxjava相关的依赖,如果你的项目不使用rxjava可以不用依赖
// 注意adapter-rxjava3,如果你的rxjava版本是2,那这里是adapter-rxjava2,这个自己网上找吧
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // rxjava call
.addConverterFactory(GsonConverterFactory.create()) // json解析
.build()
具体如何定义可以参考Retrofit 官方文档介绍学习相关注解的使用方法
public interface IService {
@GET("app/update")
Observable<Integer> getUpdateInfo();
@GET("app/test")
Call<Integer> getTestInfo();
}
IService apiService = retrofit.create(IService.class);
apiService.getUpdateInfo()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Integer integer) {
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
}
通过上面四步我们可以使用Retrofit发起一个完整的网络请求, 接下来让我们学习一下源码
Retrofit.java
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
Retrofit.java 的 Retrofit.Builder
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 对应 Builder.client() 如果你赋值的话,这里就不会为null
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
// 没有定义的话默认使用OkHttpClient
callFactory = new OkHttpClient();
}
// 对应 Builder.callbackExecutor(), 如果你赋值的话,这里就不会为null
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// platform 是Android, defaultCallbackExecutor() --> new MainThreadExecutor()
// MainThreadExecutor 创建一个主线程的Looper Handler,当异步任务处理完毕会把结果通过post的方式发到主线程
callbackExecutor = platform.defaultCallbackExecutor();
}
// 对应 Builder.addCallAdapterFactory(), 我们使用了RxJava3CallAdapterFactory.create() -->
// RxJava3CallAdapterFactory
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
// java8 开始新增 CompletableFutureCallAdapterFactory + DefaultCallAdapterFactory
// java8 以下只有 DefaultCallAdapterFactory
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
converterFactories.add(new BuiltInConverters());
// 对应 Builder.addConverterFactory(), 我们使用了GsonConverterFactory.create() --> GsonConverterFactory
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
}
目前Retrofit的相关信息已配置完毕
Retrofit(
okhttp3.Call.Factory callFactory,
HttpUrl baseUrl,
List<Converter.Factory> converterFactories,
List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor,
boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
IService apiService = retrofit.create(IService.class);
多年前分析Retrofit源码的时候我也很懵逼, 啥是动态代理? 为啥要用动态代理? 从官方的介绍或者其他博主人云亦云的观点把我搞得一头雾水,其实这个问题很简单!不是因为动态代理才有需求,而是因为需求才有动态代理
假设你是Retrofit的开发人员, 现在使用Retrofit的人都会给你传参数(url,requestMethod,header,params), 你该怎么设计框架才可以拿到用户的参数?
大部分人的第一反应就是: 暴露个方法呗! 那要是参数可变呢?重载! 那url散落代码各处梳理不便呢?em…
聪明的你开始想到了注解!创建一个网络请求接口A,里面每个方法对应每个网络请求,方法的注解对应着需要的参数,再创建一个实现类Aimpl,在Aimpl的每个方法中去反射获取A对应方法的注解然后去发起请求。
我们继续思考一下,Aimpl除了解析不同的方法外,基本上内容完全相同?如果存在接口ABCD,是不是还得对应生成Aimpl Bimpl Cimpl Dimpl这样的实现类?有没有什么办法可以再优化一下? 有! 这就是动态代理
篇幅有限这里就不展开动态代理的官方介绍了,有兴趣的同学可以自行查询下。
public <T> T create(final Class<T> service) {
// 这里会校验这个class是不是接口,不是接口就会直接抛异常
validateServiceInterface(service);
// Proxy.newProxyInstance 动态代理典型用法, 内部通过newInstance反射创建一个实例
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
// 这个InvocationHandler相当于一个Hook,只要你调用了这个实例的方法都会回调invoke
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类的话就不hook了,执行Object的方法吧
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
// platform 是Android, isDefaultMethod 判断这个接口的方法是不是default方法, Java8开始支持接口内增加有实现体的 default方法,这里的意思是: 我只拦截没有实现的方法,有实现体的方法不会拦截
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
// loadServiceMethod 是重头戏的开始,等会继续分析
: loadServiceMethod(method).invoke(args);
}
});
}
apiService.getUpdateInfo() --> 触发上文提到的 loadServiceMethod
ServiceMethod<?> loadServiceMethod(Method method) {
// retrofit不会无脑create一次,反射一次,也会有缓存机制的
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);
}
}
return result;
}
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 对注解的校验&&解析headers,method,baseUrl,contentType 等信息
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// ....
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
这里有对Kotlin suspend函数的处理,以后再讲,我们还是分析Java的流程
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
// ....
// 结合3.1 创建Retrofit实例的代码
// Retrofit.callAdapter --> Retrofit.nextCallAdapter --> callAdapterFactories.get().get()
// --> RxJava3CallAdapter
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
// Retrofit.responseBodyConverter --> Retrofit.nextResponseBodyConverter --> converterFactories.responseBodyConverter --> GsonResponseBodyConverter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
// callFactory 默认OkHttpClient
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
// 回到3.2 loadServiceMethod(method)最终返回的是 CallAdapted, 而CallAdapted没有实现invoke方法而是其父类HttpServiceMethod实现
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else{
//.....
}
}
@Override
final @Nullable ReturnT invoke(Object[] args) {
// OkHttpCall是Retrofit对OkHttp的包装
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
// adapt是抽象方法,具体实现还是交给了CallAdapted
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
// 3.3.2 提到 callAdapter 就是 RxJava3CallAdapter
return callAdapter.adapt(call);
}
@Override
public Object adapt(Call<R> call) {
// 由于我们使用的 RxJava3CallAdapterFactory.create() 默认 isAsync 传了true,所以这里最终返回的就是 CallEnqueueObservable
Observable<Response<R>> responseObservable =
isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);
// ...
} else {
observable = responseObservable;
}
// ...
return RxJavaPlugins.onAssembly(observable);
}
}
所以 apiService.getUpdateInfo() 最终返回的是 CallEnqueueObservable
我在Rxjava线程切换原理终于在2023年有了答案这篇博客中提到:Rxjava最重要的一个环节就是subscribe, 正是有了这个环节才会产生 订阅流和回调流
// 直接去看 CallEnqueueObservable 的 subscribeActual , 这里如果跟不上的话可以去看一下我的Rxjava的博客
apiService.getUpdateInfo()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Integer integer) {
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
@Override
protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
Call<T> call = originalCall.clone();
// 根据 3.3.3 call就是OkHttpCall
CallCallback<T> callback = new CallCallback<>(call, observer);
observer.onSubscribe(callback);
if (!callback.isDisposed()) {
// 真正发起网络请求的地方开始了
call.enqueue(callback);
}
}
@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 = parseResponse(rawResponse);
callback.onResponse(OkHttpCall.this, response);
//...
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
});
}
private okhttp3.Call createRawCall() throws IOException {
// 结合代码 3.3.2 callFactory 其实就是 OkHttpClient --> RealCall.newRealCall 这里就进入了OkHttp的代码,以后还会分析OKHttp,这里就到此为止
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
// ... 这会处理code码,比如 code < 200 || code >= 300 code == 204 || code == 205等
// 根据3.3.2 responseBodyConverter --> GsonResponseBodyConverter 就是这里的 responseConverter
T body = responseConverter.convert(catchingBody);
// ...
}
@Override
public T convert(ResponseBody value) throws IOException {
// Json解析
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader);
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally {
value.close();
}
}
拿到请求结果后再根据RxJava的回调流回调给订阅者完成整个请求的全部流程
Retrofit 源码分析
一定能看懂的 Retrofit 最详细的源码解析!
Android 网络框架之Retrofit源码解析
【面试 反思】Retrofit源码与设计 7 连问