NetAndorid的好东西Rx系列

拆轮子系列 - 如何由浅入深探索 Retrofit 源码?

2017-01-15  本文已影响1539人  Goo_Yao

前言

相信点开文章的你,多少也知道或者使用过Retrofit框架吧。如果你有一颗想对源码一探究竟的心,却不知如何入手。笔者一直也有这样的困惑,也正在努力总结出一套有效的分析源码方法,希望本文的拆轮子思路能给你带来一些帮助。

本文记录了笔者基于 Retrofit 2.1.0 源码的学习分析过程,文中拆解思路很大程度受到Stay4it所制作的教程启发(良心教程,文末有链接,配合视频食用本文效果更佳),故跟随该教程结合自身见解进行一番记录,如有错漏,恳请指正。

探索方式:

  1. 学会用该框架,动手写Demo,理解框架应用场景,基本特点(前提)。
  2. 利用网络资源,搜寻多篇前人写过的拆轮子资源,下载源码,跟随资源进行源码跟踪,如果不动手,我认为是很难将框架理解的。
  3. 阅读源码时,学会总结:从Retrofit一般使用方式入手,通过断点调试,观察源码执行顺序,忽略非重要代码,摸清源码主线实现思路后,再深入探索其中的细节实现。
  4. 回顾与整理,最后再过一遍思路,若走通了,那么框架的大概就理解了,同时整理笔记,便于日后忘了回来查看。

网络请求框架的基本流程

  1. 构建Request,入队
  2. 进入Executor执行,Looper不断循环,拿到Request执行
  3. 执行结果的解析与返回

Retrofit 概述(WHAT)

一个在Android/Java平台上运行的type-safe HTTP Client,其中type-safe是通过声明泛型来保证的,支持RxJava,解耦程度高,基于 Retrofit当前最新版2.1.0,即将出版2.2.0。(2017.1.)

构建Request

Retrofit通过注解来构建Request,所有的参数都可以简单配置完毕,上层不需要关心底层使用何种方式实现构建Request过程。

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

实现Executor

实现上述声明的接口,得到可以做具体请求操作的实现类。

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

相关概念
CallAdapter:Adapter a Call into the type of T.(将一个Call适配为另外一个Call的适配器接口)

HTTP Call

调用上述实现类的方法即可得到Call,而Retrofit内部使用OkHttp实现HTTP Call,最后通过Converter转换成用户想要的对象体。
Call:An invocation of a Retrofit method that sends a request to a webserver and returns a response.(请求发送与响应返回方法的调用)

Call<List<Repo>> repos = service.listRepos("octocat");

主线用例

// 源码剖析 - 步骤1~4
Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://gank.io/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    // 源码剖析 - 步骤5~6
    Api api = retrofit.create(Api.class);
    // 源码剖析 - 步骤7
    Call<BaseModel<ArrayList<Benefit>>> call = api.defaultBenefits(20, page++);

    call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {
                 @Override
                 public void onResponse(Call<BaseModel<ArrayList<Benefit>>> call, Response<BaseModel<ArrayList<Benefit>>> response) {
                     if (action == PullRecycler.ACTION_PULL_TO_REFRESH) {
                         mDataList.clear();
                     }
                     if (response.body().results == null || response.body().results.size() == 0) {
                         recycler.enableLoadMore(false);
                     } else {
                         recycler.enableLoadMore(true);
                         mDataList.addAll(response.body().results);
                         adapter.notifyDataSetChanged();
                     }
                     recycler.onRefreshCompleted();
                 }

                 @Override
                 public void onFailure(Call<BaseModel<ArrayList<Benefit>>> call, Throwable t) {
                     recycler.onRefreshCompleted();
                 }
             }
);

Retrofit主线源码剖析(HOW - Part 1)

  1. Retrofit对象的创建
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://gank.io/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
  1. build()方法内部
    构建Retrofit对象主要思路:1、创建callbackExecutor(内部获取主线程MainLooper来构建Hanlder,其execute方法本质是Handler.post(runnable),待用于线程切换);2、构建adapterFactories集合,将defaultAdapterFactory加入其中(ExecutorCallAdapterFactory类,线程切换关键实现,内部持有OkHttp代理delegate,在delegate.enqueue中的onRespond方法内使用刚刚创建的callbackExecutor.execute方法,从而实现线程切换)
public Retrofit build() {
  ...
  // 创建OkHttp,目前Retrofit只支持OkHttp
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }
  
  // 创建Executor,见步骤3
  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }

  // Make a defensive copy of the adapters and add the default Call adapter.
  // 对adapters进行保护性拷贝,并且加入默认的call adapter(使用上面创建的Executor来构建,可以认为是把主线程中的Handler传入)
  // 构建adapterFactories集合,将defaultAdapterFactory加入其中,见步骤4
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // Make a defensive copy of the converters.
  // 对converters进行保护性拷贝
  // 一般传入的为GsonConverterFactory对象,其作用主要是将json转换成java对象
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

  //最终完成Retrofit对象构建
  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}
  1. Executor的获取 - platform.defaultCallbackExecutor();
static class Android extends Platform {
    @Override
    public Executor defaultCallbackExecutor() {
        // 返回MainThreadExecutor
        return new Platform.Android.MainThreadExecutor();
    }

    @Override
    CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
        return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
        // 从主线程得到MainLooper,构建Handler
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(Runnable r) {
            // execute方法本质:通过handler,在主线程上执行该runnable
            handler.post(r);
        }
    }
}
  1. 详解 platform.defaultCallAdapterFactory(callbackExecutor) - 线程切换的关键
// platform.defaultCallAdapterFactory(callbackExecutor)
CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
  if (callbackExecutor != null) {
    return new ExecutorCallAdapterFactory(callbackExecutor);
  }
  return DefaultCallAdapterFactory.INSTANCE;
}
// ExecutorCallAdapterFactory构造方法
// ExecutorCallAdapterFactory通过传参,得到callbackExecutor
ExecutorCallAdapterFactory(Executor callbackExecutor) {
  this.callbackExecutor = callbackExecutor;
}

 // get方法,创建并返回Android平台默认CallAdapter
 @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);
      }
    };
  }

 //----------------------------分割线-----------------------------------
 // ExecutorCallAdapterFactory内部类ExecutorCallbackCall中enqueue方法,子线程转换为主线程的关键
@Override 
public void enqueue(final Callback<T> callback) {
  if (callback == null) throw new NullPointerException("callback == null");
  // 子线程中执行,此处的delegate本质是OkHttpCall(主要用于对接OkHttp框架)
  delegate.enqueue(new Callback<T>() {
    @Override 
    public void onResponse(final Call<T> call, final Response<T> response) {
      // 响应请求,调用callbackExecutor的execute
      // 从步骤3中可知,该方法本质是调用主线程的Handler执行runnable
      // 包裹用户传入的callback,从而实现线程切换 
      callbackExecutor.execute(new Runnable() {
      @Override 
      public void run() {
          if (delegate.isCanceled()) {
            // Emulate OkHttp's behavior of throwing/delivering an IOException oncancellation.
            callback.onFailure(call, new IOException("Canceled"));
          } else {
            callback.onResponse(call, response);
          }
        }
      });
    }

    @Override public void onFailure(final Call<T> call, final Throwable t) {
      // 包裹用户传入的callback,从而实现线程切换 
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          callback.onFailure(call, t);
        }
      });
    }
  });
}
  1. 详解:retrofit.create(Api.class)
    创建接口实现类:创建并返回一个会拦截接口方法的动态代理对象实例 - 接口方法被调用时,会被其拦截,内部将接口方法适配成HTTP Call,再构造对应的OkHttpCall,最后通过CallAdapter.adapt()转换成Retrofit适用的call delegates(即ExecutorCallbackCall)。
Api api = retrofit.create(Api.class);
// create方法内部实现,返回一个动态代理实例
public <T> T create(final Class<T> service) {
    ...
    // 该动态代理会对接口方法进行拦截
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        // 创建一个InvocationHandler,接口方法被调用时会被拦截调用
        new InvocationHandler() {
          private final Platform platform = Platform.get();
    // 当Api接口方法被调用时,会调用invoke方法拦截
          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            ...
            // 通过解析api方法注解、传参,将接口中方法适配成HTTP Call,详解见步骤6
            ServiceMethod serviceMethod = loadServiceMethod(method);
            // 拦截下来之后,内部构建一个okHttpCall
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            // 最后通过callAdapter将okHttpCall转换成为Retrofit适用的call delegates(代理),Android平台默认使用ExecutorCallAdapterFactory,adapt返回ExecutorCallbackCall(即步骤4)
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
  1. 详解 ServiceMethod serviceMethod = loadServiceMethod(method);
    ServiceMethod整体思路: 内部主要是将方法中的注解取出,转换成HTTP Call的逻辑,暂且不深究。
    下面看loadServiceMethod方法的实现
  // 内部有缓存以提高性能,避免重复解析 - 使用LinkedHashMap来存储ServiceMethod,与接口中方法一一对应
  private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
  ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      // 通过传入的方法,从Map中取出对应的ServiceMethod
      result = serviceMethodCache.get(method);
      if (result == null) {
    // 如果还没有实现,则构造一个ServiceMethod实例放入Map中
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
  1. ** 回到主线**,retrofit.create()方法创建好实现类后,调用接口方法获得Call,前面步骤5已经说道,当接口方法被调用时候,会被拦截并且转换成HTTP Call。
    下面我们使用call进行enqueue操作:此处的call,即ExecutorCallbackCall,它有execute(同步)、enqueue(异步)两个方法,其中enqueue方法,可达到子线程请求,成功后切换回主线程的效果(原理 - 步骤4),免去了开启线程、使用handler跨线程通信的操作。
Call<BaseModel<ArrayList<Benefit>>> call = api.defaultBenefits(20, page++);
call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {
                // 该处的onResponse方法已经转换到主线程上了,而转换关键在于Retrofit对象构建时,defaultAdapterFactory内部实现,见步骤2->步骤4
                 @Override
                 public void onResponse(Call<BaseModel<ArrayList<Benefit>>> call, Response<BaseModel<ArrayList<Benefit>>> response) {
                    ...
                 }

                 @Override
                 public void onFailure(Call<BaseModel<ArrayList<Benefit>>> call, Throwable t) {
                    ...
                 }
             }
  1. Retrofit主线流程总结:
    1. Retrofit对象的构建 - Retrofit.Builder()...build():构建OkHttpClient,目前Retrofit仅支持OkHttpClient;②构建Executor:优先根据用户提供的callBackExcecutor来构建,若用户没有提供,则提供defaultCallbackExecutor(其内部会获取MainLooper构建handler,execute方法直接handler.post(runnable),实现在主线程上的操作);③使用executor来构建adapterFactories集合,优先将用户提供的adapterFactory加入到其中,再加上defaultCallAdapterFactory(传入②创建的callbackExecutor,defaultCallAdapterFactory内部持有OkHttpCall,在其enqueue方法中的onResponse方法调用defaultCallbackExecutor.execute方法,从而实现线程切换操作);④最终使用Retrofit构造方法构建Retrofit实例对象

2. Retrofit接口方法的实现方式 - retrofit.create(接口名.class):create方法创建并返回动态代理对象实例,动态代理对象内部会拦截接口方法的调用②动态代理内部通过ServiceMethod将接口方法适配成HTTP Call,再构造对应的OkHttpCall,最后通过CallAdapter转换成Retrofit适用的call delegate(ExecutorCallbackCall)。

3. 使用动态代理(接口实现类)调用接口方法得到Call、使用call.enqueue进行异步请求:①调用接口方法时,动态代理对象(接口实现类)内部拦截;②调用call.enqueue,内部会调用ExecutorCallAdapter的enqueue方法,enqueue中onResponse方法调用defaultCallbackExecutor.execute方法,使用主线程Handler.post(runnable)从而实现线程切换操作

  1. Retrofit主线流程总结图解(从上往下阅读):

Retrofit源码深入探索(HOW - Part 2)

  1. retrofit.create()方法中一段令人疑惑的代码,却是Retrofit工作主要代码
// ①创建ServiceMethod,见深入探索步骤2
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
// ②传入ServiceMethod构造OkHttpCall,使用构造方法传入成员变量
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
// ③又将构造好的OkHttpCall传回,通过ServiceMethod中的callAdapter来进行转换,见步骤4
return serviceMethod.callAdapter.adapt(okHttpCall);
  1. 解析ServiceMethod:Adapts an invocation of an interface method into an HTTP call - 将接口中的方法适配成HTTP Call
    loadServiceMethod内部通过Builder,build()方法创建:内部创建了CallAdapter(例:defaultCallAdapterFactory - 将Http Call 转成Retrofit中的Call->调用okHttp3.Call->线程切换)、Converter(请求结果转换器,例:Gson)、callFactory(okhttp3.Call.Factory - 对接okHttp)、
    public ServiceMethod build() {
      // callAdapter的创建
      callAdapter = createCallAdapter();//追踪代码可知:本质通过retrofit.nextCallAdapter方法创建,见步骤3
      responseType = callAdapter.responseType();
      ...
      // 创建ResponseConverter(转换器 - 对象与Http表示形式相互转换)
      responseConverter = createResponseConverter();

      // 解析注解参数,逻辑不必深究
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      ...
      return new ServiceMethod<>(this);
    }
// 构造方法,内部参数基本都由Retrofit提供
ServiceMethod(Builder<R, T> builder) {
    // 负责创建HTTP请求,一个可以执行的HTTP请求(okHttp3.Call),仅支持OkHttpClient
    this.callFactory = builder.retrofit.callFactory();
    // 将Retrofit中的Call<T>转为T(Retrofit中的Call ≠ okHttp中的Call,前者表示retrofit接口方法调用,内部通过后者实现,最后通过)
    this.callAdapter = builder.callAdapter;// 将响应类型适配成用户指定的类
    this.responseConverter = builder.responseConverter;// 对象与Http表示形式相互转换的转换器
    this.parameterHandlers = builder.parameterHandlers;// 解析接口注解参数
    ...// 略去非重点关注成员变量
  }
  1. CallAdapter的创建:遍历adapterFactories,找到并返回合适的adapter
public CallAdapter<?, ?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    ...
    //  遍历adapterFactories,找到可以接收指定类型的adapter
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      // 通过get方法,获得相匹配的类型
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        // 找到合适adapter,返回
        return adapter;
      }
    }
    ...
}
//下面看看一般的adapterFactories源码
//1、ExecutorCallAdapterFactory中的get方法
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    // 如果返回类型为Call类型,则使用它
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    ...
}
//2、RxJavaCallAdapterFactory、RxJavaCallAdapterFactory中的get方法是Observable类型(源码略显复杂掠过)
  1. 解析serviceMethod.callAdapter.adapt方法:因callAdapter类型而异,ExecutorCallAdapterFactory适配的是ExecutorCallbackCall类型,而RxJava适配的是Observable类型
    注意:无论是什么类型的callAdapter,最终都会调用OkHttpCall中的enqueue、execute方法,把实际的工作交给OkHttp来做,详见步骤5
// ExecutorCallAdapterFactory
@Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
// RxJavaCallAdapter
@Override public Object adapt(Call<R> call) {
    OnSubscribe<Response<R>> callFunc = new CallOnSubscribe<>(call);

    OnSubscribe<?> func;
    // 构造订阅者func逻辑,省略...
    // 通过func构造Observable
    Observable<?> observable = Observable.create(func);
    // Observable配置逻辑,省略...
    return observable;
  }
  1. 解析OkHttpCall调用链:OkHttp的包装类,与OkHttp对接,实现了Retrofit中的Call接口,内部使用OkHttp框架来进行实际操作,解析好数据后再调用传入的callbackA -(内部通过Execute实现线程切换)->调用用户传入的callbackInMainThread(漫长的一个调用链..)
 // 从上面分析可知,调用接口定义方法返回ExecutorCallbackCall        
 // call.enqueue(callbackInMainThread);  调用其enqueue方法,内部持有OkHttpCall 作为 delegate
// 将callbackInMainThread包裹executor实现线程切换,生成callbackA
// delegate.enqueue(callbackA); 上面的方法内部会调用delegate.enqueue方法以对接OkHttp框架
// 下面看OkHttpCall的enqueue方法(OkHttp框架里的方法,与Retrofit无关)
@Override public void enqueue(final Callback<T> callback) {
     ...
    okhttp3.Call call;
    ...
    call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
      // onResponse时候进行数据转换,关键点:Converter (下方附有调用链)
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }
        ...
    // 请求失败调用callbackA.onFailure -(内部线程切换)-> callbackInMainThread.onFailure
      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    // 响应成功调用callbackA.onResponse -(内部线程切换)-> callbackInMainThread.onSuccess或onFailure
      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

 // 数据转换调用链:parseResponse -(关键调用)-> serviceMethod.toResponse -> GsonRequestBodyConverter.convert
 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
      ...
      T body = serviceMethod.toResponse(catchingBody);
      ...
    } 

Retrofit中的设计模式,解耦方式(WHY)

Retrofit中涉及不少的设计模式,笔者能力有限,设计模式方面知识储备不足,仍需继续努力,这里暂且给出设计模式定义与链接,作初步认识。

门面模式(Facade Pattern)

Retrofit - retrofit.create()
定义:外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。门面模式又称为外观模式,它是一种对象结构型模式。
传送门:xingjiarong - 门面模式

工厂模式

Retrofit中的三大工厂 - okhttp3.Call.Factory,CallAdapter.Factory 和 Converter.Factory
定义:又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式(同属于创建型模式的还有工厂方法模式,抽象工厂模式,单例模式,建造者模式)。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
传送门:xingjiarong - 简单工厂模式xingjiarong - 抽象工厂模式

装饰模式

ExecutorCallbackCall类:在源“OkHttpCall”操作时,进行线程切换操作
定义:动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。
传送门:design-patterns - 装饰模式

动态代理

ExecutorCallbackCall中的delegate的动态设置,从而针对不同代理采用不同方法
定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
传送门:xiazdong - 代理模式及Java实现动态代理

适配器模式

CallAdapter,各种适配器,将一个东西适配成一个适合我们使用的东西
定义:适配器模式将一个类的接口,转化成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
传送门:xingjiarong - 适配器模式

策略模式

CallAdapter内部还夹杂着策略模式?
定义:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
传送门:xingjiarong - 策略模式design-patterns - 策略模式

Stay的详细分析:Retrofit分析-经典设计模式案例

学习资源

上一篇下一篇

猜你喜欢

热点阅读