前言
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
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对象。
原文链接:https://mp.weixin.qq.com/s/diT6tn3EGO41r-b7TAJ8Yg