美文网首页Android
[搞定开源] 第三篇 retrofit 2.4.0与设计模式

[搞定开源] 第三篇 retrofit 2.4.0与设计模式

作者: 展翅而飞 | 来源:发表于2018-10-08 18:18 被阅读0次

Android开源项目原理系列

[搞定开源] 第一篇 okhttp 3.10原理
[搞定开源] 第二篇 okio 1.14原理

demo

interface Api {
    @GET("v2/movie/top250")
    fun getTopList(): Call<HttpResponse>
}

api用的是豆瓣一个接口。

val retrofit = Retrofit.Builder()
        .baseUrl("http://api.douban.com")
        .addConverterFactory(GsonConverterFactory.create(Gson()))
        .build()

val api = retrofit.create(Api::class.java)
val call = api.getTopList()
call.enqueue(object : Callback<HttpResponse> {
    override fun onResponse(call: Call<HttpResponse>, response: Response<HttpResponse>) {
        println("response:${response.body()}")
    }

    override fun onFailure(call: Call<HttpResponse>, t: Throwable) {
    }
})

网络请求工作实际由OkHttp负责,Retrofit是对网络请求接口的封装,简单的几行就可以发送一次网络请求。

归纳

面向对象的网络请求流程可以归纳为:

  1. 封装网络请求参数为对象;
  2. 发送网络请求,同步或者异步,得到服务器响应结果;
  3. 解析结果为对象。

Retrofit的工作是第1、3点,OKHttp负责第2点。具体化Retrofit的工作流程为下面5点,接下来正文也是通过这5点详述:

  1. 创建Retrofit实例;
  2. 通过Java接口描述请求api,创建请求实例;
  3. 同步或者异步发送网络请求;
  4. 数据转换器解析数据;
  5. 切换线程

理解Retrofit源码主干并不困难,我也不打算深入到每个细节。Retrofit是设计模式的宝库,是我本文最想分析总结的。

  • Retrofit外观模式;
  • Retrofit、ServiceMethod使用构建者模式创建;
  • 动态代理实例化请求实例;
  • 工厂模式创建CallAdapter适配器;
盗了张图

1、创建Retrofit

创建Retrofit实例用的是构建者设计模式,主要关注有哪些参数,默认参数是什么,哪些参数是必须的。

  • platform:当前平台,预设了Java8和Android,不同平台的初始参数有区别
  • baseUrl:必填
  • callFactory:默认是OkHttpClient
  • converterFactories:将响应结果进行转换
  • callAdapterFactories:对请求返回类型进行转换
  • callbackExecutor:异步回调执行

整个库围绕的是核心类Retrofit,只需要了解这个入口类就可以玩起来,内部复杂的逻辑不需要考虑,这是外观模式的体现。

2、网络请求的描述

每个网络请求api在Retrofit中是定义在接口里,我们知道需要调用retrofit.create()将接口实例化。这种操作比较新奇,具体是怎样实现的?

public <T> T create(final Class<T> service) {
  Utils.validateServiceInterface(service);
  if (validateEagerly) {
    eagerlyValidateMethods(service);
  }
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
        private final Platform platform = Platform.get();

        @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.adapt(okHttpCall);
        }
      });
}

答案是通过动态代理模式,运行时生成接口的代理类。当调用api里的方法时,自动调用动态代理的invoke。一般的动态代理会有具体的实现类,代理类在调用实现类前后加上诸如日志的操作,这里就不需要了。

invoke核心是最后三行代码:

  • api里每个请求方法和网络参数,包装为ServiceMethod类。ServiceMethod不是直接创建,而是通过一个名叫serviceMethodCache的Map进行缓存,保持单例。真要创建时,ServiceMethod使用了Builder设计模式,它包含了网络请求的详细参数,具体创建逻辑不一一道来;
  • ServiceMethod和请求参数包装为retrofit2.OkHttpCall;
  • 返回是接口retrofit2.Call,具体的类型需要通过CallAdapter转换,比如返回RxJava的Observable。
CallAdapter

CallAdapter是Retrofit混杂着最多设计模式的一个接口,它的作用是将请求返回类型retrofit2.Call<R>转为合适的类型T。顾名思义,首先它是一个适配器。

回看上面demo定义的一个api,默认返回Call<HttpResponse>,使用RxJava就返回一个Observable。如果我们有需要,完全可以自行开发新的CallAdapter,返回MyCall<MyResponse>类型。

@GET("v2/movie/top250")
fun getTopList(): Call<HttpResponse>

看一眼源码里的retrofit-adapters文件夹,Retrofit支持转换的库有:

  • guava
  • java8
  • rxjava
  • rxjava2
  • scala

CallAdapter的创建需要用到CallAdapter.Factory,工厂模式,默认工厂类是ExecutorCallAdapterFactory:

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

工厂get函数返回CallAdapter的匿名实现类,将Object转为ExecutorCallbackCall,这也是demo里call的类型。ExecutorCallbackCall的代码不太多,大部分函数都委托给delegate处理,唯独处理了异步请求的enqueue函数。

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.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.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);
            }
          });
        }
      });
    }
  }

delegate实质就是OkHttpCall,这里应用了装饰器模式,OkHttpCall原来的enqueue函数回调是在子线程执行,ExecutorCallbackCall装饰了enqueue函数,将回调交由回调执行器处理,在Android平台也即是MainThreadExecutor,最终回调执行在主线程。(线程切换后文第5点)

3 发送请求

获取retrofit2.Call对象后,就可以发送网络请求了,分同步和异步两个方法。毕竟Retrofit的定位是对OkHttp的封装,收集的网络请求信息转换为okhttp3.Call交给OkHttp处理,最终得到OkHttp返回的Response,中间过程看OkHttp相关文章。

异步请求和同步请求类似,唯一不同的是执行网络请求后,回调需要在指定的线程执行。

还有最后一步数据转换调用了parseResponse函数,将OkHttp的Response转为Retrofit的Response,实质调用了我们定义的Converter。

4 数据转换Converter

数据转换的作用是将结果ResponseBody转为合适的类型,默认的转换器是BuiltInConverters,几乎什么都无干。我常用的是GsonConverter,具体的解析过程不看了,

5 线程切换

异步得到服务器响应后,需要将回调运行在合适的线程。根据平台的不同,提供不同的处理。

static class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override public void execute(Runnable r) {
      handler.post(r);
    }
}

Android平台默认的CallbackExecutor是MainThreadExecutor,通过Handler切换线程到UI主线程。

用RxJava2CallAdapterFactory就更加方便了,自行设置。

相关文章

网友评论

    本文标题:[搞定开源] 第三篇 retrofit 2.4.0与设计模式

    本文链接:https://www.haomeiwen.com/subject/vmpjaftx.html