先看一个问题,Retrofit2 中定义的接口能够直接返回一个ResponseBody
的String
吗?html
public interface RestClientV1 {
@GET("order/detail")
String getOrderDetail(@Query("orderId") long orderId);
}
复制代码
若是你不能确定的回答能够,同时不能清楚的知道该怎么作,很是推荐阅读这篇文章。这是一篇 Retrofit2 的进阶用法的文章,若是不熟悉 Retrofit2 的基本用法,建议先去 官网 看一下教程,再过来看这篇文章。若是你正在考虑如何使用 Retrofit2 来封装一个网络层,这篇文章讲到的示例、原理和设计思想应该会很是适合你。java
咱们先看两段源码:android
CallAdapter.java
public interface CallAdapter<R, T> {
Type responseType();
T adapt(Call<R> call);
abstract class Factory {
public abstract @Nullable CallAdapter<?, ?> get(Type returnType,
Annotation[] annotations,
Retrofit retrofit);
//省略
}
}
复制代码
这个适配器的做用是将一个R
,转化为一个自定义类型T
;其中的适配器工厂会根据returnType
(就是上面接口中定义的返回类型)来返回相应的适配器。git
Converter.java
public interface Converter<F, T> {
T convert(F value) throws IOException;
abstract class Factory {
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {
return null;
}
//省略
}
}
复制代码
这个转换器的做用就是将F
对象转换为T
对象;其中转换器工厂中的responseBodyConverter(..)
须要根据type
(就是咱们自定义的响应结果类型)来返回相应的转换器,而且这个转换器中的F
被指定为了ResponseBody
。github
你们可能对这两个接口的设计和做用仍是有点困惑,不要紧,下面我们会反复说到这两个接口。api
下面开始写咱们的实现代码:网络
StringCallAdapterFactory
和StringCallAdapter
public class StringCallAdapterFactory extends CallAdapter.Factory {
@Nullable
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if(returnType == String.class)
return new StringCallAdapter();
return null;
}
class StringCallAdapter implements CallAdapter<String,String>{
@Override
public Type responseType() {
return String.class;
}
@Override
public String adapt(Call<String> call) {
try {
return call.execute().body();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
}
复制代码
咱们在StringCallAdapterFactory
的get(..)
方法中,当发现接口的返回类型是String
时,返回咱们自定义的StringCallAdapter
,而StringCallAdapter
,顾名思义就是Call
(retrofit2.Call
)的一个适配器,做用就是将Call转化成String
(这个逻辑具体是在adapt(..)
方法里面处理)。特别地,responseType()
方法的做用是告诉Converter
,我须要一个String.class
的响应数据。
app
StringConverterFactory
和StringConverter
public class StringConverterFactory extends Converter.Factory {
@Nullable
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == String.class) {
return new StringConverter();
}
return null;
}
class StringConverter implements Converter<ResponseBody, String> {
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
}
}
复制代码
类似的,咱们在StringConverterFactory
的responseBodyConverter(..)
方法中,当发现参数type
(就是上一步的responseType()
方法返回值)是String
的时候,返回一个自定义的StringConverter
,这个适配器的做用是把 http 响应数据ResponseBody
转化为String
,其实现也很简单,直接调用ResponseBody.string()
方法便可。框架
当咱们在写跟界面无关的代码的时候,特别推荐使用单元测试来验证逻辑的正确性,这是一件省时省力,又能够有效确保质量的作法。下面是咱们写的一个简单的测试示例:异步
@Before
public void create() {
mockWebServer = new MockWebServer();
mockWebServer.setDispatcher(new MockDispatcher());
OkHttpClient client = new OkHttpClient.Builder().build();
restClientV1 = new Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.client(client)
.addCallAdapterFactory(new StringCallAdapterFactory())
.addConverterFactory(new StringConverterFactory())
.build()
.create(RestClientV1.class);
}
@Test
public void test() {
System.out.println(restClientV1.getOrderDetail(1));
}
复制代码
运行结果: hi man,this is order detail(orderId=1)
完整可运行的代码在这里
分析框架原理的时候,通常我会先去找切入点,或者是疑惑点。基于上面的例子,有 3 个疑惑点:
RestClientV1.getOrderDetail(..)
方法返回类型(称为returnType
) 是如何起做用的?CallAdapterFactory
是如何起做用的?ConverterFactory
是如何起做用的?下面咱们顺着这个思路来查看相应源码:
returnType
到CallAdapter<T, R>
在源码中咱们先找到获取CallAdapter<T, R>
的地方
private CallAdapter<T, R> createCallAdapter() {
Type returnType = method.getGenericReturnType();
//省略
Annotation[] annotations = method.getAnnotations();
try {
//<-关键代码
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
复制代码
代码中的 method 对象就是
RestClientV1.getOrderDetail(..)
方法
上面关键代码处以returnType
为参数调用了retrofit.callAdapter(..)
方法获取一个适配器,接着看
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
}
复制代码
核心是这一行adapterFactories.get(i).get(returnType, annotations, this)
,就是循环调用CallAdapter.Factory
的 get 方法来获取一个可用的适配器,一旦找到就返回。注意这里的returnType
就是String.class
,再回过头来看咱们以前的 2.1 的代码,
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if(returnType == String.class)
return new StringCallAdapter();
return null;
}
复制代码
这时咱们就拿到了一个StringCallAdapter
对象。拿到后还作了一件事情,再看代码
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
//省略
responseConverter = createResponseConverter();
复制代码
其中把callAdapter.responseType()
方法的结果存了下来,而后调用了createResponseConverter()
responseType
到Converter<F, T>
再看createResponseConverter()
方法代码:
private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create converter for %s", responseType);
}
}
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
}
复制代码
经过上面的代码追踪,咱们知道在retrofit.responseBodyConverter(..)
中的responseType
参数正是StringCallAdapter
中返回的String.class
,再看核心的获取Converter
的方法,跟获取CallAdapter
的方法基本一致,结合 2.2 的代码
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == String.class) {
return new StringConverter();
}
return null;
}
复制代码
显然,这时会返回一个StringConverter
对象。
CallAdapter<T, R>
和Converter<F, T>
如何起做用?这时框架已经获取到的StringCallAdapter
和StringConverter
,而后它们会被存放在一个ServiceMethod
对象中
final class ServiceMethod<R, T> {
final CallAdapter<R, T> callAdapter;
private final Converter<ResponseBody, R> responseConverter;
}
复制代码
咱们再看看这两个对象分别是在哪一个地方起做用的
CallAdapter<T, R>
的做用处public <T> T create(final Class<T> service) {
//省略部分代码
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
//省略部分代码
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);//<- 关键代码在这里
}
});
}
复制代码
上面的代码使用动态代理返回了service
接口的一个实现类,调用restClient.getOrderDetail(1)
方法时,它的返回值就是invoke(..)
方法的返回值,这个返回值就是调用StringCallAdapter.adapt(..)
的返回值。再看 2.1 中我们自定义适配器的代码
@Override
public String adapt(Call<String> call) {
try {
return call.execute().body();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
复制代码
咱们直接将body
以String
的形式返回了。Retrofit
这个设计很是的巧妙和灵活,咱们能够将一个 http 请求包装成任何对象返回,能够选择包装的时候直接执行 http 请求(就像咱们这个例子),也可使其再调用新的包装对象的某个方法再执行 http 请求(好比自带的Call
)。
Converter<F, T>
的做用处前面咱们看到实际执行 http 请求的是OkHttpCall
,在请求执行完成后有一段解析Response
的代码
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
//省略代码
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);//<- 关键代码在这里
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
复制代码
这个方法的做用是将okhttp3.Response
转化为retrofit2.Response
。
由于 Retrofit2 最终也是使用 Okhttp 来发起 http 请求的。 再看上面关键代码的实现
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
复制代码
其实很简单,直接调用了咱们自定义的StringConverter
来获取一个自定义对象,再看 2.2 中自定义的转换器的代码
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
复制代码
这个也很是简单,就很少解释了。
最后我们再用图形的方式来捋一下整个调用流程:
前面这个例子咱们在实际中并不会这么用,下面咱们使用 Retrofit2
来作一个有实用价值的 App 网络层封装,在这以前咱们先看看在网络层封装时基本都会遇到的几个问题:
Activity
/Fragment
)已被销毁时致使的崩溃为了解决上面的问题,咱们设计本身的Call
接口,在看Call
接口的代码前,有几点须要说明下:
public class ApiResponse<T> {
/** * api业务响应状态 */
private String status;
/** * api业务数据 */
private T content;
/** * api业务响应失败错误码 */
private String errorCode;
/** * 错误字符串信息 */
private String errorMsg;
}
复制代码
正常状况下,服务端会返回请求业务成功( ok )或者失败( fail );异常状况下,客户端增长一个 error 的 status ,用于表示非 200 的请求以及调用抛出异常的状况
ProgressOperation
接口public interface ProgressOperation {
/**加载数据失败*/
void showFailed();
/**加载数据成功*/
void showContent();
/**开始加载数据*/
void showProgress();
}
复制代码
这样当咱们须要使用不一样的界面 展现形式(如:
ProgressDialog
或者Progressbar
)来实现 Loading 的时候,只要分别作一个实现类便可
下面是自定义的Call
接口代码:
public interface Call<T> {
/** * 设置http200 ok的回调 */
Call<T> ok(@NonNull Observer<T> observer);
/** * 设置http200 fail的回调 */
Call<T> fail(@NonNull Observer<ApiResponse<T>> observer);
/** * 设置error的回调 */
Call<T> error(@NonNull Observer<ApiResponse<T>> observer);
/** * 设置进度监听 */
Call<T> progress(@NonNull ProgressOperation progressOperation);
/** * 执行异步请求,绑定组件生命周期(获取所有状态结果) */
void enqueue(@NonNull LifecycleOwner owner, @NonNull Observer<ApiResponse<T>> observer);
/** * 执行异步请求,绑定组件生命周期(获取部分状态结果) */
void enqueue(@NonNull LifecycleOwner owner);
/** * 执行异步请求,但不须要绑定组件生命周期(获取部分状态结果) */
void enqueue();
/** * 执行异步请求,但不须要绑定组件生命周期(获取所有状态结果) */
void enqueue(@NonNull Observer<ApiResponse<T>> observer);
/** * 发起同步网络请求 */
ApiResponse<T> execute();
/** * 取消请求 * 一、对于单个http请求,取消时若是尚未开始执行,则不执行;若是在执行中,则会确保执行结束不会回调,不确保必定能被取消 * 二、对于多个连续http请求,除了1的特性外,取消后剩下的未开始执行请求也不会被执行 */
void cancel();
/** * 是否被取消 * * @return */
boolean isCancelled();
}
复制代码
上面的代码注释已经比较详细,你们能够仔细看下
LiveData
为基础作了单个请求Call
的实现类,实现这块就再也不讲解,能够有多种实现方式,有兴趣你们能够自行查看源码。下面我们看一下如何使用这个Call
restClientV1.ok("1").progress(progress).ok(content -> {
System.out.println(content.getName());
}).enqueue(lifecycleOwner);
复制代码
其中
lifecycleOwner
能够直接使用 supportv26 包中的Activity
或Fragment
,传入这个对象后,若是组件已处于 destroy 状态,则回调不会被执行
Task task1 = ((lifeState, apiResponse) -> restClientV1.okArray("1").execute());
Task task2 = ((lifeState, apiResponse) -> restClientV1.ok("1").execute());
Call<Content> call = MergeCall.task(task1, task2);
call.enqueue(lifecycleOwner,apiResponse -> {
if(apiResponse.isOk()){
//更新UI
}else{
//显示错误信息
}
});
复制代码
上面咱们使用了本身定义的
Task
接口来描述一个任务,执行多个任务的时候,只有上个任务成功,才会执行下一个任务,不然会直接执行回调
public class CheckTokenInterceptor implements Call.Interceptor {
public static final CheckTokenInterceptor INSTANCE = new CheckTokenInterceptor();
/** * 返回true表示中止下一步执行 */
@Override
public boolean preExecute() {
return false;
}
/** * 对于异步请求,返回true表示中止下一步执行 */
@Override
public boolean onResponse(ApiResponse apiResponse) {
return checkTokenExpired(apiResponse.getErrorCode());
}
private boolean checkTokenExpired(String errorCode) {
//检查token是否过时
return false;
}
}
复制代码
而后能够这样设置拦截器
Retrofit
对象时调用方法addCallAdapterFactory(new CustomCallAdapterFactory(CheckTokenInterceptor.INSTANCE))
;MergeCall
的时候传入拦截器public static Call task(Executor executor, List<Interceptor> interceptors, Task... tasks) {
return new MergeCall(Arrays.asList(tasks), interceptors, executor);
}
public static Call task(Task... tasks) {
return task(AsyncTask.THREAD_POOL_EXECUTOR, tasks);
}
//<- 关键在这里
public static Call task(Executor executor, Task... tasks) {
return MergeCall.task(executor, Arrays.asList(CheckTokenInterceptor.INSTANCE), tasks);
}
复制代码
小结
从上面对于单个和多个串行请求的设计和用法中能够看到,咱们解决了前面提到的4个问题。在这种设计下,咱们使用本身的Call
接口作为网络层和其它层交互的纽带,其它层(Presenter
/ViewModel
、Activity
/Fragment
)彻底不知道底层使用的是什么网络框架,那么若是哪天有一个更好用的网络框架,咱们替换起来也是很是方便。最后再放一下本文 demo 的 源码连接。
在 demo 中,你们能够从 test 目录下相应的测试用例开始看起
立刻过年了,祝你们新年快乐^_^