本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。
前言
Retrofit是一个基于AOP思想,对RestfulApi注解进行动态代理的网络框架;
今天我们就来探讨下实现原理,一起进步
一、使用Retrofit
1、包引用
在gradle文件中引用retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:retrofit-converters:2.3.0' compile 'com.squareup.retrofit2:retrofit-adapters:2.3.0'
如果需要使用更多扩展功能,比如gson转换,rxjava适配等,可以视自己需要继续添加引用;
compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
如果现有的扩展包不能满足需要,还可以自己扩展converter,adapter等;
2、定义接口
Retrofit要求定义一个网络请求的接口,接口函数里要定义url路径、请求参数、返回类型;
public interface INetApiService { @GET("/demobiz/api.php") Call<BizEntity> getBizInfo(@Query("id") String id); }
在这个接口定义中,用注解@GET("/demobiz/api.php")声明了url路径,用注解@Query("id") 声明了请求参数;
最重要的是,用Call声明了返回值是一个Retrofit的Call对象,并且声明了这个对象处理的数据类型为BizEntity,BizEntity是我们自定义的数据模型;
3、依次获得Retrofit对象、接口实例对象、网络工作对象
首先,需要新建一个retrofit对象;
然后,根据上一步的接口,实现一个retrofit加工过的接口对象;
最后,调用接口函数,得到一个可以执行网络访问的网络工作对象;
//新建一个Retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN)//要访问的网络地址域名,如http://www.zhihu.com .addConverterFactory(GsonConverterFactory.create()) .build(); ... //用retrofit加工出对应的接口实例对象 INetApiService netApiService= retrofit.create(INetApiService.class); //可以继续加工出其他接口实例对象 IOtherService otherService= retrofit.create(IOtherService.class); ··· //调用接口函数,获得网络工作对象 Call<BizEntity> callWorker= netApiService.getBizInfo("id001");
这个复杂的过程下来,最终得到的callWorker对象,才可以执行网络访问。
4、访问网络数据
用上一步获取的worker对象,执行网络请求
callWorker.enqueue(new Callback<BizEntity>() { @Override public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {...} @Override public void onFailure(Call<BizEntity> call, Throwable t) {...} });
在回调函数里,取得我们需要的BizEntity数据对象
二、Retrofit实现原理
1、Retrofit对象的构建就是简单的builder模式,直接看create
//Retrofit.java public <T> T create(final Class<T> service) { //验证 validateServiceInterface(service); return (T) //动态代理 Proxy.newProxyInstance( service.getClassLoader(), //类加载器 new Class<?>[] {service}, //一组接口 new InvocationHandler() { //判断android和jvm平台及其版本 private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object[] args){ //如果该方法是Object的方法,直接执行不用管 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } //isDefaultMethod:检查是否是java8开始支持的接口默认方法 return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); //我们关注这里 } }); }
Proxy.newProxyInstance动态代理,运行期会生成一个类(字节码)如$ProxyN,实现传入的接口即WanApi,重写接口方法然后转发给InvocationHandler的invoke
class $ProxyN extends Proxy implements WanApi{ Call<WanArticleBean> articleList(@Path("page") int page){ //转发给invocationHandler invocationHandler.invoke(this,method,args); } }
2、validateServiceInterface验证逻辑
//Retrofit.java private void validateServiceInterface(Class<?> service) { //检查:WanApi不是接口就抛异常... //检查:WanApi不能有泛型参数,不能实现其他接口... if (validateEagerly) { //是否进行严格检查,默认关闭 Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { //遍历WanApi方法 //不是默认方法,并且不是静态方法 if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { //把方法提前加载进来(检查下有没有问题) loadServiceMethod(method); } } } }
如果开了validateEagerly,会一次性把接口WanApi的所有方法都检查一遍并加载进来,可以在debug模式下开启,提前发现错误写法,比如在@GET请求设置了@Body这种错误就会抛出异常:
java.lang.IllegalArgumentException: Non-body HTTP method cannot contain @Body.
3、loadServiceMethod
然后是loadServiceMethod(method).invoke(args),看名字可知是先找方法,然后执行
//Retrofit.java //缓存,用了线程安全ConcurrentHashMap final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>(); ServiceMethod<?> loadServiceMethod(Method method) { ServiceMethod<?> result = serviceMethodCache.get(method); //WanApi的articleList方法已缓存,直接返回 if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { //解析articleList的注解,创建ServiceMethod并缓存起来 result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); } } return result; }
跟进ServiceMethod.parseAnnotations
//ServiceMethod.java static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { //1. RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); //检查:articleList方法返回类型不能用通配符和void... //2. return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); }
4、 RequestFactory.parseAnnotations
//RequestFactory.java static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { return new Builder(retrofit, method).build(); } class Builder { RequestFactory build() { //解析方法注解如GET for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } //省略各种检查... //解析参数注解如Path int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) { parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter); } //省略各种检查... return new RequestFactory(this); } }
得到RequestFactory后, HttpServiceMethod.parseAnnotations,HttpServiceMethod负责适配和转换处理,将接口方法的调用调整为HTTP调用
//HttpServiceMethod.java //ResponseT响应类型如WanArticleBean,ReturnT返回类型如Call static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { //省略kotlin协程逻辑... Annotation[] annotations = method.getAnnotations(); //遍历找到合适的适配器 CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); //得到响应类型,如WanArticleBean Type responseType = callAdapter.responseType(); //遍历找到合适的转换器 Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType); okhttp3.Call.Factory callFactory = retrofit.callFactory; return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }
5、最终返回了一个CallAdapted,看到CallAdapted
//CallAdapted extends HttpServiceMethod extends ServiceMethod class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> { private final CallAdapter<ResponseT, ReturnT> callAdapter; CallAdapted( RequestFactory requestFactory, okhttp3.Call.Factory callFactory, Converter<ResponseBody, ResponseT> responseConverter, CallAdapter<ResponseT, ReturnT> callAdapter) { super(requestFactory, callFactory, responseConverter); this.callAdapter = callAdapter; } @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) { //适配器 return callAdapter.adapt(call); } }
回到Retrofit.Builder
//Retrofit.Builder.java public Retrofit build() { Executor callbackExecutor = this.callbackExecutor; //如果没设置线程池,则给android平台设置一个默认的MainThreadExecutor(用Handler将回调切回主线程) if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); //添加默认的DefaultCallAdapterFactory callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); }
DefaultCallAdapterFactory这个工厂创建具体的CallAdapter实例
//DefaultCallAdapterFactory.java public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType); //如果指定了SkipCallbackExecutor注解,就表示不需要切回主线程 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,他的enqueue会使用主线程池的execute return executor == null ? call : new ExecutorCallbackCall<>(executor, call); } }; }
6、invoke
前边loadServiceMethod得到了CallAdapted,然后执行invoke,实现在父类HttpServiceMethod里,
//HttpServiceMethod.java final ReturnT invoke(Object[] args) { //终于见到okhttp了! Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); return adapt(call, args); } class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> { private final CallAdapter<ResponseT, ReturnT> callAdapter; @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) { //用前边得到的适配器,把OkHttpCall包成ExecutorCallbackCall return callAdapter.adapt(call); } }
然后是请求入队,ExecutorCallbackCall.enqueue -> OkHttpCall.enqueue,
//ExecutorCallbackCall.java void enqueue(final Callback<T> callback) { delegate.enqueue( new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { //将回调切回主线程 callbackExecutor.execute( () -> { callback.onResponse(ExecutorCallbackCall.this, response); }); //... } @Override public void onFailure(Call<T> call, final Throwable t) {} }); } //OkHttpCall.java void enqueue(final Callback<T> callback) { //okhttp逻辑 okhttp3.Call call; call.enqueue(new okhttp3.Callback() { void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { callback.onResponse(OkHttpCall.this, response); } }) }
到此整个流程就通了
二、功能扩展
1、OkHttpClient
Retrofit使用OkHttpClient来实现网络请求,这个OkHttpClient虽然不能替换为其他的网络执行框架比如Volley,但是Retrofit允许我们使用自己扩展OkHttpClient,一般最常扩展的就是Interceptor拦截器了;
OkHttpClient mClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { try { Request.Builder builder = chain.request().newBuilder(); builder.addHeader("Accept-Charset", "UTF-8"); builder.addHeader("Accept", " application/json"); builder.addHeader("Content-type", "application/json"); Request request = builder.build(); return chain.proceed(request); } catch (Exception e) { e.printStackTrace(); } return null; } }).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .client(mClient) .build();
2、addConverterFactory
扩展的是对返回的数据类型的自动转换,把一种数据对象转换为另一种数据对象;
GsonConverterFactory可以把Http访问得到的json字符串转换为Java数据对象BizEntity,这个BizEntity是在INetApiService接口中要求的的;
如果现有的扩展包不能满足需要,可以继承Retrofit的接口。retrofit2.Converter
在创建Retrofit对象时,可以插入我们自定义的ConverterFactory;
//retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定义Converter .build();
3、addCallAdapterFactory
扩展的是对网络工作对象callWorker的自动转换,把Retrofit中执行网络请求的Call对象,转换为接口中定义的Call对象;
这个转换不太好理解,我们可以对照下图来理解:
Retrofit本身用一个OkHttpCall的类负责处理网络请求,而我们在接口中定义需要定义很多种Call,接口里的Call和Retrofit里的OkHttpCall并不一致,所以我们需要用一个CallAdapter去做一个适配转换;
这其实是Retrofit非常核心,也非常好用的一个设计,如果我们在接口中要求的函数返回值是个RxJava的Flowable对象
public interface INetApiService { @GET("/demobiz/api.php") Flowable<BizEntity> getBizInfo(@Query("id") String id); }
那么我们只需要为Retrofit添加对应的扩展;
//retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
就能得到Flowable类型的callWorker对象;
//用retrofit加工出对应的接口实例对象 INetApiService netApiService= retrofit.create(INetApiService.class); ··· //调用接口函数,获得网络工作对象 Flowable<BizEntity> callWorker= netApiService.getBizInfo("id001"); 在这里,callAdapter做的事情就是把retrofit2.Call对象适配转换为Flowable<T>对象;
同样,如果现有的扩展包不能满足需要,可以继承Retrofit的接口;retrofit2.CallAdapter
4、动态替换url
在构建Retrofit时传入HttpUrl对象,之后这个实例就一直存在不会更改,所以可以反射修改他的字段比如host,来实现动态替换服务端地址;
String SERVER = "https://www.xxx.com/"; HttpUrl httpUrl = HttpUrl.get(SERVER); Retrofit retrofit = new Retrofit.Builder() //.baseUrl(SERVER) .baseUrl(httpUrl) //使用HttpUrl .build();
总结:
1.Retrofit将Http请求抽象成java接口
2.接口里用注解 描述和配置网络请求参数
3.动态代理的方式来生成call对象。