美文网首页NetAndorid的好东西Rx系列
拆轮子系列 - 如何由浅入深探索 Retrofit 源码?

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

作者: Goo_Yao | 来源:发表于2017-01-15 16:35 被阅读1539次

    前言

    相信点开文章的你,多少也知道或者使用过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的适配器接口)

    • Type responseType():将返回的请求转化为参数Type类型
    • T adapt(Call<R> call):将一个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分析-经典设计模式案例

    学习资源

    相关文章

      网友评论

      本文标题:拆轮子系列 - 如何由浅入深探索 Retrofit 源码?

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