从 Retrofit 源码中学习设计模式(上)

本文从Retrofit 2.0.0-beta4的源码出发,探讨其设计模式,包括Builder模式、动态代理、适配器模式和装饰者模式。详细分析了Retrofit的配置、Api的使用和线程切换等过程,揭示了Retrofit的强大和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面


Retrofit 是我用过最友好的网络请求框架。无论是优雅的注解Api,还是强大而灵活的拓展性,都是其流行的一大要素。现如今,它的火爆程度完全不亚于 Rxjava。对于它的成功,api 的友好性是一方面,更深层的原因在于其精妙的解耦,而它的源码更是设计模式的教科书!
我乘机研究了一下源码,同时参考了一些博客,想扒一扒这个轮子的设计理念。

摘要


本文基于 Retrofit 2.0.0-beta4,主要涉及这三个包:

dependencies {
    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' // Retrofit网络处理
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4' // Retrofit的rx解析库
    compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' // Retrofit的gson库
}



常规的网络请求包含这些步骤,Retrofit 也不例外,但我们不会按照这个顺序来解析源码,而是从使用的角度入手:

  • 新建 Request 请求体
  • 在 io 线程执行请求(即新建 Thread 或 ThreadPool ),获得 Response
  • 从 Response 里解析数据
  • 回调到 UI 线程

之后的分析中,你会看到如下设计模式:

  • Builder 模式
  • 动态代理
  • 工厂模式
  • 适配器模式
  • 装饰者模式


源码分析


0. Retrofit 的配置,Builder 模式

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL) // 必须
        .addConverterFactory(GsonConverterFactory.create()) // 可选
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 可选
        .client(new OkHttpClient()) // 默认
        .callbackExecutor(new MyCallbackExecutor()) // 可选
        .build();

private class MyCallbackExecutor implements Executor {
    @Override
    public void execute(Runnable runnable) {
        runnable.run();
    }
}

这是一段经典的Retrofit配置,可以看到支持的配置项有:

  • baseUrl
  • ConverterFactory: Gson 解析
  • CallAdapterFactory: RxJava 支持
  • OkHttpClient: 请求内核的替换
  • callbackExecutor: 线程切换方式,默认给你 post 到 UI 线程

正如官方所说,这种依赖注入的方式,使得框架的拓展性非常好。

  • 感觉 json 不好用,分分钟换成 xml 协议解析。
  • 感觉默认的 Callback 不好用,分分钟换成 RxJava;还不好用?再换成 Guava试试。

如图:
这里写图片描述

顺便一提,Builder模式是对一系列setXXX()的封装,常用于复杂对象的构建,这一点在Universal-Image-Loader里体现的尤为明显。其优点在于build完之后,该对象便处于不可修改的状态,保证了后续操作的安全性。

1. Api 的使用,动态代理

这里借用一下项目 https://github.com/Dimon94/GanWuMei 里的例子:
配置好retrofit以后,我们会这样使用它:

// 1. 定义一个 API 接口
public interface RestAPI {
    @GET("data/福利/"+ 10 +"/{page}")
    Observable<ImageData> getImageData(
            @Path("page") int page);
}

// 2. new 出 RestAPI 的对象,并使用之
RestAPI restAPI = retrofit.create(RestAPI.class); // 动态代理,获取代理对象
restAPI.getImageData(page) // 像普通的 RestAPI 的对象一样,直接调用方法
        .subcribe(imageData -> onNext(ImageData)); // 2.1 采用 Observable 的异步请求与回调

第一次见可能觉得奇怪,只见接口,不见实现体。这种对客户端隐藏实现体的方式被称为”动态代理”,是代理模式的一种变种。

不了解的童鞋可以先看这篇文章:
公共技术点之 Java 动态代理

我们跟进retrofit.create(Class<?>)里,可以看到Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)的影子。在匿名类InvocationHandler中,按照动态代理的尿性,应该调用return method.invoke(delegate, args);。然而,事实上并没有,因为根本不存在我们定义的RestAPI的真正实现。事实上,它选择了 new 一个Observable<?>实例给我们:

// 1. In Retrofit.create()
new InvocationHandler() {
  @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
    return loadMethodHandler(method).invoke(args); // 我们转到 MethodHandler.invoke(Object...) 里
  }
});

// 2. In MethodHandler.java
Object invoke(Object... args) {
  return callAdapter.adapt(
      new OkHttpCall<>(callFactory, requestFactory, args, responseConverter)); 
      // 这里直接依赖了 OkHttpCall(), 其实还可以通过依赖注入进一步来解耦。
      // 我们顺势跳到 RxJavaCallAdapterFactory.SimpleCallAdapter.adapt(Call<R>) 里
}

// 3. In RxJavaCallAdapterFactory.java
static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
  @Override 
  public <R> Observable<R> adapt(Call<R> call) {
    // 我们看到 new 了一个 Observable 出来
    return Observable.create(new CallOnSubscribe<>(call)) 
        .flatMap(new Func1<Response<R>, Observable<R>>() {
          @Override public Observable<R> call(Response<R> response) {
            if (response.isSuccess()) {
              return Observable.just(response.body());
            }
            return Observable.error(new HttpException(response));
          }
        });
  }
}

总结下来就是,通过create()我们能得到api的实例,同时在调用方法时给我们提供了Observable的默认实现。
再另外解释一下第2步:callAdapter.adapt()直接寻路到RxJavaCallAdapterFactory里边,你可能觉得奇怪。这是由于,我们在配置的时候有这么一步retrofit.addCallAdapterFactory(RxJavaCallAdapterFactory.create())(这是可选的)。此时我们的callAdapter已然是RxJavaCallAdapterFactory.SimpleCallAdapter了。关于这段逻辑可以自己细看MethodHandler.create()

2. Api 的使用,非RxJava的情形,适配器模式

之前说到retrofit.addCallAdapterFactory()并非必选项,那么不设置的时候,用的就不是Rxjava的方式,而是内置的CallCallback的回调方式:

// 1. 定义一个 API 接口, 注意到返回值不是 Observable, 而是内置的 Call
public interface RestAPI {
    @GET("data/福利/"+ 10 +"/{page}")
    Call<ImageData> getImageData(
            @Path("page") int page);
}

// 2. new 出 RestAPI 的对象,并使用之
RestAPI restAPI = retrofit.create(RestAPI.class); // 动态代理,获取代理对象
restAPI.getImageData(0) // 像普通的 RestAPI 的对象一样,直接调用方法
        .enqueue(new Callback<ImageData>() { // 2.1 采用 Call 的异步请求与回调
            @Override
            public void onResponse(Call<ImageData> call, Response<ImageData> response) {}

            @Override
            public void onFailure(Call<ImageData> call, Throwable t) {}
        });

你可能注意到了,再看前面的第2步:callAdapter.adapt(),这次的callAdapter又是啥呢?

有必要先看一下CallAdapter<T>这个类:

public interface CallAdapter<T> {
  // 转接口:把 Call<R> 转换成 T
  <R> T adapt(Call<R> call);

  abstract class Factory {
    public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);
  }
}

// 此前的 Observable 转接口
class SimpleCallAdapter implements CallAdapter<Observable<?>>{
  @Override 
  public <R> Observable<R> adapt(Call<R> call) {...}
}

可以看到,CallAdapter<T>是为了适配Observable才加入的,原本内置的OkHttp请求会返回Call<T>,为了适配Observable,我们做了转接。那么现在不配置RxJava的情况下,我们需要返回的就是Call<T>,是不是代表这个callAdapter.adapt()啥都不做呢?你可以姑且这么认为,后边还会提到一些别的细节。

3. 线程切换,切换到 UI 线程,装饰者模式

如果Retrofit到此为止了,恐怕其友好度会大打折扣吧。你是否注意到回调回来的onResponse()仍在io线程,又得用handler.post()麻烦的要死。作者自然考虑到了这一点。

还记得1. Api 的使用,非RxJava的情形,适配器模式里有这样一段代码么:

// 2. In MethodHandler.java
Object invoke(Object... args) {
  return callAdapter.adapt(
      new OkHttpCall<>(callFactory, requestFactory, args, responseConverter)); 
}

按照上一节所讲,应该可以直接return new OkHttpCall<>(...);。然而,为了切换线程,作者还是机智的做了一下适配,我们来看ExecutorCallAdapterFactory

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    return new CallAdapter<Call<?>>() {
      // 汗,输入 Call<R> 输出还是 Call<R>,根本没变嘛
      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

需求是这样的,通过callAdapter.adapt(new OkHttpCall<>(...)),我们希望得到一个增加了线程切换功能的Call
注意重点来了,通常我们为一个类拓展功能有两种方式:

  • 继承:子类直接继承OkHttpCall,拓展功能。子类直接依赖父类,依赖严重!!!
  • 组合:新类依赖OkHttpCall的接口,并与OkHttpCall实现同一个接口,然后拓展功能,这便是装饰着模式。和代理模式挺像哦。

这里写图片描述

这里的ExecutorCallbackCall便是这样一个装饰者:

// In ExecutorCallAdapterFactory.java
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) {
    delegate.enqueue(new Callback<T>() {
      @Override public void onResponse(final Call<T> call, final Response<T> response) {
        callbackExecutor.execute(new Runnable() { // 关键点在这,execute()做了线程切换
          @Override public void run() {
            if (delegate.isCanceled()) {
              callback.onFailure(call, new IOException("Canceled"));
            } else {
              callback.onResponse(call, response);
            }
          }
        });
      }

      @Override public void onFailure(final Call<T> call, final Throwable t) {
        callbackExecutor.execute(new Runnable() { // 关键点在这,execute()做了线程切换
          @Override public void run() {
            callback.onFailure(call, t);
          }
        });
      }
    });
  }
  // 其余接口都只是简单的调用 delegate 的相应方法,就不贴了
}

再看一下execute()的实现你就懂了,它相当与post()一个Runnable出去,于是切换到了UI线程

class PlatForm{
  static class Android extends Platform {
    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      if (callbackExecutor == null) {
        callbackExecutor = new MainThreadExecutor();
      }
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

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

      // 看,这里简单的 post 了一下,就切到 UI 线程了
      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }
}

当然这个callbackExecutor也是可以自定义的,你试一下不post,会不会报io线程更新UI的错。

总结


源码中设计模式的点大体都覆盖到了,不过关于请求流程上还有细节没讲,放到下一篇里讲好了:

  • 新建 Request 请求体(解析注解)
  • 在 io 线程执行请求(即新建 Thread 或 ThreadPool ),获得 Response
  • 从 Response 里解析数据

总体而言,作者的设计模式玩的很6啊,解耦的也很棒,值得多次回味。

参考资料


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值