Retrofit源码分析

栏目: IOS · Android · 发布时间: 5年前

内容简介:在学习了首先我们聊一下对于一个优秀的libary,应该怎样一步一步地分析它。在上面的视频中,介绍了一个很好的方法。分为三个阶段:通过了解、深入、思考,将整个框架一层一层地剖析出来,由浅入深,最后从全局再看这个框架,或许我们会不自禁的赞叹代码的美妙。

在学习了 Retrofit分析-漂亮的解耦套路(视频版) 后,自己又仔细的钻研了一下Retrofit的源码,也大致清楚了Retrofit进行网络请求的步骤。好记性不如烂笔头,以文章的形式将对于Retrofit的思考记录下来,也加深理解。(如有错误,请不吝赐教)

分析源码的一般姿势

首先我们聊一下对于一个优秀的libary,应该怎样一步一步地分析它。在上面的视频中,介绍了一个很好的方法。分为三个阶段:

  • what: 这个框架是用来干什么的,暴露给用户的API都是用来干嘛的。
  • how: 这个API内部是怎样实现的,整个框架正常调用时内部是一个怎样的流程。
  • why: 为什么要这样实现,有什么好处,可不可以有其他的实现,与原来的实现相比怎么样。

通过了解、深入、思考,将整个框架一层一层地剖析出来,由浅入深,最后从全局再看这个框架,或许我们会不自禁的赞叹代码的美妙。

Retrofit使用(what)

详细的了解Retrofit使用及API可以到Retrofit的Github。

简单使用

首先,我们需要创建一个请求接口,内部是一个请求方法,通过注解的方式定义请求类型及url

public interface IHttpRequestTest {
    @GET("api/data/福利/{number}/{page}")
    Call<BaseModel<ArrayList<Benefit>>> getBenefits(@Path("number") int number, @Path("page") int page);
}
复制代码

然后我们就可以愉快地使用Retrofit。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://gank.io/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
IHttpRequestTest iHttpRequestTest = retrofit.create(IHttpRequestTest.class);
Call<BaseModel<ArrayList<Benefit>>> call = iHttpRequestTest.getBenefits(40, 2);
call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {
    @Override
    public void onResponse(@NonNull Call<BaseModel<ArrayList<Benefit>>> call, @NonNull Response<BaseModel<ArrayList<Benefit>>> response) {
        if(response.body() != null && response.body().results != null){
            myAdapter.setData(response.body().results);
        }
    }

    @Override
    public void onFailure(@NonNull Call<BaseModel<ArrayList<Benefit>>> call, Throwable t) {
        Toast.makeText(MainActivity.this, "请求失败:" + t.getMessage(),Toast.LENGTH_SHORT).show();
    }
});
复制代码

一般http的请求步骤

Retrofit源码分析

如上图,对于一般http请求:

  • 生成request请求,包括请求类型,url等等,放入Excutor队列
  • 请求在Excutor中循环进行httpcall请求
  • 等待请求回调

接下来,我们就可以根据这个一般的http请求步骤,通过debug的形式弄清楚Retrofit的内部逻辑。

Retrofit源码解析

探索Retrofit对象初始化时参数的真正含义

首先第一步,就是生产Retrofit对象,并传入基本的参数。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://gank.io/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
复制代码

进入Retrofit类的源码,可以看到build方法

public Retrofit build() {
    if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
    }
    
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
        callFactory = new OkHttpClient();
    }
    //如果callbackExcutor为空,创建默认的callbackEcxutor
    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
    }

    // Make a defensive copy of the adapters and add the default Call adapter.
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    //添加默认的callAdapterFactories
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

    // Make a defensive copy of the converters.
    List<Converter.Factory> converterFactories = new ArrayList<>(
        1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
    converterFactories.addAll(this.converterFactories);
    //如果没有自定义converterFactories,将使用默认的converterFactories
    converterFactories.addAll(platform.defaultConverterFactories());
    //返回Retrofit对象,包括在Builder中添加的自定义参数。
    return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
        unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
复制代码

可以看到,生成的retrofit对象有五个参数必须要存在。

  • baseUrl 就不用多说了,是必须的。
  • callFactory 使用的是OkHttp3中的 OkHttpClient
  • callbackExcutorcallAdapterFactory 中的 callAdapter 都对应一般请求步骤中的 Excutor ,只不过 callbackExcutor 用于在请求内部回调中切换线程,回调的方法存在哪个线程中取决于 callbackExcutor 在哪个线程。比如请求要在子线程中,回调的方法在主线程中。
  • callAdapter 是真正的请求对象, callbackExcutor 相当于在 callAdapter 中切换线程。
  • converterFactory 是用来解析返回的json数据的。当然这些具体的解释在下面的分析中都会看到。

有源码可看出,默认的 callbackExcutorcallAdapterFactoryconverterFactory 添加都与 platform 对象有关。这是一个 Platform 类,进入研究一下。

class Platform {
  private static final Platform PLATFORM = findPlatform();
  static Platform get() {
    return PLATFORM;
  }
  
  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
  
  @Nullable Executor defaultCallbackExecutor() {return null;}

  List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
      @Nullable Executor callbackExecutor) {...}

  List<? extends Converter.Factory> defaultConverterFactories() {...}

  @IgnoreJRERequirement // Only classloaded and used on Java 8.
  static class Java8 extends Platform {...}

  static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
        @Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
      return Build.VERSION.SDK_INT >= 24
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
    }

    @Override List<? extends Converter.Factory> defaultConverterFactories() {
      return Build.VERSION.SDK_INT >= 24
          ? singletonList(OptionalConverterFactory.INSTANCE)
          : Collections.<Converter.Factory>emptyList();
    }

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

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }
}
复制代码

内容有点多,这里我只提取了对现在有用的。整体来看, Platform 内部有两个子类继承了该类 Android与 Java 8 。还有一个get方法,获取Platform静态实例,静态实例调用findPlatform()方法。这个方法是根据程序所执行的环境决定是返回一个 Android 实例还是 Java8 实例或者 Platform 实例。这里我们的环境是 Android ,所以具体看 Android 类中的方法。

  • defaultCallbackExecutor()方法,返回了 MainThreadExecutor 类实例。此类在Android类内部,可以清晰的看出其中的handler静态变量是用的主线程的looper。也就是说,默认的 callbackExcutor 是运行在主线程的。
  • defaultCallAdapterFactories()方法,参数是上面得到的 callbackExcutor 。方法内部创建了 ExecutorCallAdapterFactory 类实例,并返回该实例的List。
  • defaultConverterFactories()方法,返回了 OptionalConverterFactory 实例,此类中的转换 converter() 方法只检查了参数是否为空就将其返回了,说明默认的回调数据是不进行任何转换的。

至此,三个默认参数已确定。

获得自定义接口对象

IHttpRequestTest iHttpRequestTest = retrofit.create(IHttpRequestTest.class); 这是接下来的执行步骤,自定义接口对象由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();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
复制代码

该方法返回的是通过反射获得自定义接口的代理类,并在类中重写了invoke方法。这表明当该类中的方法被执行时,就会拦截此方法。

拦截并解析方法。

Call<BaseModel<ArrayList<Benefit>>> call = iHttpRequestTest.getBenefits(40, 2); 由上一步骤可知,当执行代理类中的方法时,会被拦截。并最终执行 loadServiceMethod(method).invoke(args != null ? args : emptyArgs); 方法并返回。先看一下 loadServiceMethod(method) 方法。

ServiceMethod<?> loadServiceMethod(Method method) {
    //serviceMethodCache,如果执行过此方法,就会有缓存存在这里,直接取出即可
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
复制代码

嗯...,除了前面与缓存有关的东西,又跑到了 ServiceMethod 类中的 parseAnnotations(this, method) 方法里。

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,
          "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract T invoke(Object[] args);
}
复制代码

这是一个抽象类,第一句 RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); ,是解析方法构建request参数的。RequestFactory类中内容比较多,简单讲一下就是通过获得方法注解、返回类型、方法参数,分别进行解析。

最后它又跑到 HttpServiceMethod 类中了。此类继承了 ServiceMethod 类。

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    Type responseType = callAdapter.responseType();

    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
  }
  
  private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method) {
    Type returnType = method.getGenericReturnType();
    Annotation[] annotations = method.getAnnotations();
    
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
  }

  private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
      Retrofit retrofit, Method method, Type responseType) {
    Annotation[] annotations = method.getAnnotations();
    
    return retrofit.responseBodyConverter(responseType, annotations);
  }
复制代码

简化了异常检测类代码,可以看到最后返回的是 HttpServiceMethod 类的实例对象,并将retrofit中的一些默认参数传了进去。

解析完了 loadServiceMethod(method) ,接下来就是执行invoke方法了。由于 loadServiceMethod(method) 返回的是 HttpServiceMethod 类的实例对象,所以执行的就是 HttpServiceMethod 类中的invoke方法。

@Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }
复制代码

此方法很简单,调用了callAdapter中的adapt方法,参数是一个OkHttpCall,字面意思就是将 OkHttpCall 适配为其他类。那么这个类是什么什么呢。应该是我们需要返回的 Call<BaseModel<ArrayList<Benefit>>> 类。那么怎么适配的呢。上面我们已经说到,retrofi中的默认参数 callAdapterFactoriesExecutorCallAdapterFactory 类,而 callAdapterFactories 中的 callAdapter 是通过 callAdapterFactories.get() 获得的,那么就要去这里面找了。

@Override public @Nullable 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);
      }
    };
  }
复制代码

返回的 CallAdapter 对象就是我们需要的。其中重写了adapt方法,也是我们要找的。它又返回了 ExecutorCallbackCall 对象,第一个参数就是默认运行在主线程中的 callbackExecutor ,第二个参数则是调用时传进来的 OkHttpCall 对象。所以说我们需要返回的 Call<BaseModel<ArrayList<Benefit>>> 对象就是 ExecutorCallbackCall 对象。

依靠OkHttp3进行网络请求,转换回调函数线程到主线程。

call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {...}); ,终于到最后一步了,拿着上一步返回的 Call<BaseModel<ArrayList<Benefit>>> 对象进行异步请求。调用了enqueue方法,也就是 ExecutorCallbackCall 中的enqueue方法。

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);
            }
          });
        }
      });
    }
复制代码

构造方法给了出来,变量名有些转换。 callbackExecutor 依然是在主线程中的那个 callbackExecutordelegate 是上一步传进来的 OkHttpCall 对象。而enqueue的参数callback则是我们自己传入的callback。

方法中首先是 delegate 也就是OkHttp3进行的异步请求,在回调的函数中,通过 callbackExecutor 使线程切换为主线程,再调用我们自己的callback中对应的函数,使我们可以再回调中进行操作,并且是在主线程中。

OkHttpCall 这个类就不在多讲了,主要作用是retrofit通过OkHttp3进行请求的一个转换。

结语

第一次分析源码,虽然有视频的帮助,但许多类之间跳来跳去的让人眼花缭乱,很艰难地顺了下来。有了这样一个大概的了解,接下来就要分析其设计模式,欣赏其漂亮的解耦套路。当然,如果你已经有了上面的了解,接下来看 Retrofit分析-经典 设计模式 案例 就会简单明了。


以上所述就是小编给大家介绍的《Retrofit源码分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ACM国际大学生程序设计竞赛题解

ACM国际大学生程序设计竞赛题解

赵端阳//袁鹤 / 电子工业 / 2010-6 / 38.00元

《ACM国际大学生程序设计竞赛题解(1)》可以作为高等院校有关专业的本科和大专学生参加国际大学生程序设计竞赛的辅导教材,或者作为高等院校数据结构、C/C++程序设计或算法设计与分析等相关课程的教学参考书。随着各大专院校参加ACM/ICPC热情的高涨,迫切需要有关介绍ACM国际大学生程序设计竞赛题解的书籍。《ACM国际大学生程序设计竞赛题解(1)》根据浙江大学在线题库的前80题,进行了解答(个别特别......一起来看看 《ACM国际大学生程序设计竞赛题解》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具