提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
retrofit学习笔记
一、retrofit的使用步骤
步骤1:添加Retrofit库的依赖
步骤2:创建 接收服务器返回数据 的类
步骤3:创建 用于描述网络请求 的接口
步骤4:创建 Retrofit 实例
步骤5:创建 网络请求接口实例 并 配置网络请求参数
步骤6:发送网络请求(异步 / 同步)
二、网络请求接口的书写
接口书写格式:
@方法("地址")
@标记
接收类 方法名(@注解(键名)参数)
例:
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> getCall(@Field("username") String name, @Field("age") int age);
- 请求类型有GET、POST、PUT、HEAD、OPTION、DELETE、PATH、HTTP
- 有三种标记:
标记 | 解释 | 规范 |
---|---|---|
@FormUrlEncoded | 表示请求体是一个Form表单 | 必须使用@Filed注解键名 |
@MultiPart | 表示请求体是一个支持文件上传的表单 | 使用@Part注解键名 |
@Streaming | 表示以流的形式返回,用于大量数据 |
- 参数注解
总结:@Field、@Query和@Part都适用于普通表单字段,其中Field用于POST请求,Query用于GET请求,Part用于有文件上传的表单
@Path是URL地址的缺省值,@URL可以直接作为地址传入,当GET、POST…HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
public interface GetRequest_Interface {
// RxJava 方式:Observable<..>接口形式
@GET("users/{user}/repos")
//Observable<ResponseBody> getBlog(@Path("user") String user );
Call<ResponseBody> getBlog(@Path("user") String user );
// 访问的API是:https://api.github.com/users/{user}/repos
// 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
}
三、retrofit的配置及使用
创建 retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://fanyi.youdao.com/") // 设置网络请求的Url地址
.client(sOkHttpClient)
.addConverterFactory(GsonConverterFactory.create()) // 设置数据解析器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava平台
.build();
创建网络请求接口实例,配置参数
// 创建 网络请求接口 的实例
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
//对 发送请求 进行封装
Call<Reception> call = request.getCall();
步骤6:发送网络请求(异步 / 同步)
//发送网络请求(异步)
call.enqueue(new Callback<Translation>() {
//请求成功时回调
@Override
public void onResponse(Call<Translation> call, Response<Translation> response) {
//请求处理,输出结果
response.body().show();
}
//请求失败时候的回调
@Override
public void onFailure(Call<Translation> call, Throwable throwable) {
System.out.println("连接失败");
}
});
// 发送网络请求(同步)
Response<Reception> response = call.execute();
结合RxJava发送网络请求
// 创建 网络请求接口 的实例
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
//用RXJava对 发送请求 进行封装
Observable<Translation> observable = request.getCall();
//发送异步请求
observable.subservableOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subservable(new Observable<Translation>{
// 发送请求后调用该复写方法(无论请求成功与否)
@Override
public void onSubscribe(Disposable d) {
...// 初始化工作
}
// 发送请求成功后调用该复写方法
@Override
public void onNext(Translation result) {
...// 对返回结果Translation类对象 进行处理
}
// 发送请求成功后,先调用onNext()再调用该复写方法
@Override
public void onComplete() {
Log.d(TAG, "请求成功");
}
// 发送请求失败后调用该复写方法
@Override
public void onError(Throwable e) {
Log.d(TAG, "请求失败");
}
});
}
四、retrofit源码解读
retrofit网络通信流程
具体过程
- 通过解析 网络请求接口的注解 配置 网络请求参数
- 通过 动态代理 生成 网络请求对象
- 通过 网络请求适配器 将 网络请求对象 进行平台适配(CallJavaAdapter)
- 通过 网络请求执行器 发送网络请求(OkHttp3.Call)
- 通过 数据转换器 解析服务器返回的数据(Converter)
- 通过 回调执行器 切换线程(子线程 ->>主线程)(CallBackExecutor)
- 用户在主线程处理返回结果
1.创建retrofit实例
Retrofit 使用建造者模式通过Builder类建立了一个Retrofit实例,具体创建细节是配置了:
平台类型对象(Platform - Android)
网络请求的url地址(baseUrl)
网络请求工厂(callFactory)
默认使用OkHttpCall
网络请求适配器工厂的集合(adapterFactories)
本质是配置了网络请求适配器工厂- 默认是ExecutorCallAdapterFactory
数据转换器工厂的集合(converterFactories)
本质是配置了数据转换器工厂
回调方法执行器(callbackExecutor)
默认回调方法执行器作用是:切换线程(子线程 - 主线程)
由于使用了建造者模式,所以开发者并不需要关心配置细节就可以创建好Retrofit实例,建造者模式get。
在创建Retrofit对象时,你可以通过更多更灵活的方式去处理你的需求,如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能
2.创建网络请求接口实例
2.1使用步骤
<-- 步骤1:定义接收网络数据的类 -->
<-- JavaBean.java -->
public class JavaBean {
.. // 这里就不介绍了
}
<-- 步骤2:定义网络请求的接口类 -->
<-- AccessApi.java -->
public interface AccessApi {
// 注解GET:采用Get方法发送网络请求
// Retrofit把网络请求的URL分成了2部分:1部分baseurl放在创建Retrofit对象时设置;另一部分在网络请求接口设置(即这里)
// 如果接口里的URL是一个完整的网址,那么放在创建Retrofit对象时设置的部分可以不设置
@GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
// 接受网络请求数据的方法
Call<JavaBean> getCall();
// 返回类型为Call<*>,*是解析得到的数据类型,即JavaBean
}
<-- 步骤3:在MainActivity创建接口类实例 -->
AccessApi NetService = retrofit.create(AccessApi.class);
<-- 步骤4:对发送请求的url进行封装,即生成最终的网络请求对象 -->
Call<JavaBean> call = NetService.getCall();
步骤3讲解:AccessApi NetService = retrofit.create(NetService.class);
public <T> T create(final Class<T> service) {
//从class是不是接口和有没有继承其他接口进行校验
Utils.validateServiceInterface(service);
if (validateEagerly) {
// 判断是否需要提前验证
eagerlyValidateMethods(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 {
// 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);
}
//关键部分
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
// 特别注意
// return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)
// 可以解读为:getProxyClass(loader, interfaces) .getConstructor(InvocationHandler.class).newInstance(invocationHandler);
// 即通过动态生成的代理类,调用interfaces接口的方法实际上是通过调用InvocationHandler对象的invoke()来完成指定的功能
<-- 关注点1:eagerlyValidateMethods() -->
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) { loadServiceMethod(method); }
// 将传入的ServiceMethod对象加入LinkedHashMap<Method, ServiceMethod>集合
// 使用LinkedHashMap集合的好处:lruEntries.values().iterator().next()获取到的是集合最不经常用到的元素,提供了一种Lru算法的实现
}
}
动态代理的使用:
return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)
- 通过代理模式中的动态代理模式,动态生成网络请求接口的代理类,并将代理类的实例创建交给InvocationHandler类 作为具体的实现,并最终返回一个动态代理对象。当我们调用请求接口时,InvocationHandler中的Invoke方法就会拦截到
retrofit的原理其实就是这样,当我们调用请求接口时,InvocationHandler中的Invoke方法就会拦截到方法、参数,再根据我们在方法上的注解,去拼接为一个正常的Okhttp请求,然后执行。
ServiceMethod(网络请求方法)的创建
- ServiceMethod serviceMethod = loadServiceMethod(method);这个方法首先是想查看serviceMethodCache中有没有缓存之前创建的网络请求实例,没有就通过建造者模式创建serviceMethod对象
if (result == null) {
// 下面会详细介绍ServiceMethod生成实例的过程
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
ServiceMethod.parseAnnotations
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//获取注解信息
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.");
}
//HttpService实现大部分的功能
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
可以看到通过ServiceMethod通过RequestFactory.parseAnnotations(retrofit, method);创建了一个RequestFactory实例,RequestFactory中已经包含了访问网络所需的所有基本信息,并且在通过Builder创建的过程中会对参数进行验证
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
//2、ServiceMethod文件中ServiceMethod.Builder
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations(); //方法的注解
this.parameterTypes = method.getGenericParameterTypes(); //方法的参数类型
this.parameterAnnotationsArray = method.getParameterAnnotations(); //方法参数的注解
HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);返回了一个HttpServiceMethod对象去执行invoke调用
//部分关键代码
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
...
//创建了网络请求适配器
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
...
//创建网络请求执行器
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
...
return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
//HttpServiceMethod中具体实现了invoke如下:
@Override final @Nullable ReturnT invoke(Object[] args) {
//创建Call对象
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
// adapt 也是个抽象方法,具体实现在CallAdapted,SuspendForResponse,SuspendForBody
return adapt(call, args);
}
HttpServiceMethod的SuspendForBody的实现:
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
private final boolean isNullable;
......
@Override protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
//noinspection unchecked Checked by reflection inside RequestFactory.
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
/**
像在await和awaitNullable内部一样,
对OkHttp Call.enqueue()的调用有时可以在异常返回之前调用提供的回调,
而调用栈帧可以返回。 协程将拦截Continuation的后续调用,并同步引发异常。
如果未在接口方法中声明检查的异常,则Java代理无法引发检查的异常。
为了避免将同步检查的异常包装在UndeclaredThrowableException中,
将其拦截并提供给帮助程序,该帮助程序将强制执行挂起操作,
以便可以将其传递给继续操作以绕过此限制。
**/
try {
// 调用具体的扩赞方法
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
} catch (Exception e) {
return KotlinExtensions.yieldAndThrow(e, continuation);
}
}
}
接下来是扩展方法await实现如下:
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
// 实际就是调用call.enqueue
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
val invocation = call.request().tag(Invocation::class.java)!!
val method = invocation.method()
val e = KotlinNullPointerException("Response from " +
method.declaringClass.name +
'.' +
method.name +
" was null but response body type was declared as non-null")
continuation.resumeWithException(e)
} else {
//携带数据切回挂起处,等同于callback
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
当执行到KotlinExtensions.await/awaitNullable时,就会执行相应扩展函数挂起。不难发现最终还是通过Call.enqueue发起请求,得到相应结果则通过相应的resume函数恢复挂起。这个Call对象即是上面invoke中的OkHttpCall,而它的创建方式如下。不难猜测最终发起请求和数据转换都在它里面,所以上面的await扩展中调用的enqueue方法也在其中实现。
//避免回滚上去看OkHttpCall,所以在这儿由cv了一次
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
复制代码看看OkHttpCall的enqueue方法
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
......
//经过一些列判断操作 最终发起请求
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// 解析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(); // TODO this is not great
}
}
......
});
}
// 解析响应数据
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
......
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
//responseConverter 最终通过Converter解析完毕数据
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
......
}
四、总结
Retrofit通过接口注解简化网络请求配置,采用动态代理加载解析注解执行方法。然后通过OkHttpCall发起请求,获得响应后通过Converter进行数据转换后交给CallAdapter把最终数据交给开发者。
通过对Retrofit的使用和源码了解,Retrofit确实是封装了OkHttp,并且大量使用注解和设计模式,比如建造者模式、动态代理模式、工厂模式、适配器模式等等。
虽然这是基于2.6.2解析,但是看最新提交已有些许改版,前面也提到一点儿。
五、补充
线程切换
因为上面是用的协程,所以没有看到是如何进行线程切换的,现在我们来see 1 see,注释都加了。如下:
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
// 传入参数为回调执行者,还记得在哪儿传入的么?
// Retrofit的build方法,根据相关平台里面来的
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
// 此方法用于createCallAdapter
@Override public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
//获取执行者(协程不需要)
final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
// 返回call,非协程方式用ExecutorCallbackCall包一层
return executor == null
? call
: new ExecutorCallbackCall<>(executor, call);
}
};
}
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
// 此处的delegate就是OkHttpCall
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
//回调执行者完成线程切换
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
// 通过callback回调了结果
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
@Override public boolean isExecuted() {
return delegate.isExecuted();
}
@Override public Response<T> execute() throws IOException {
return delegate.execute();
}
@Override public void cancel() {
delegate.cancel();
}
@Override public boolean isCanceled() {
return delegate.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override public Call<T> clone() {
return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}
@Override public Request request() {
return delegate.request();
}
}
}
# 总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。