Android框架系列----Retrofit

引言
  Retrofit 是 Square 公司开发的一款针对 Android 网络请求的框架,遵循 Restful 设计风格,我们查看 invoke 可以发现:底层基于 OkHttp 实现的 ,相比其他网络请求框架,有以下优势:

  • 性能最好,速度最快(动态代理优势)
  • 简洁易用,代码简化
  • 解耦彻底,职责细分
  • 易与其他框架联用(Rxjava)

当然也有弊端:因为高度封装,相对于 OkHttp ,扩展性变差了,不过整体能满足项目需求.今天我就带大家来看一下 Retrofit 究竟是如何使用,以及内部工作原理

一.概述
作用
  OKHttp 做的更加简洁,更加方便,同时影藏了 oKHttp 的某些功能。

type-safe
   retrofit 是基于 okhttp 的,因此 retrofit 所有的工作都是围绕在请求体和响应体来展开的,Retrofit 提供了各种类型的转换器以及可以自定义转换器,去构建你的请求体,以及通过转换器去序列化响应体为你想要的类型,从而保证你的请求体和响应体都是安全的

二.使用
1. 添加 retrofit 依赖
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'

2. 添加网络权限


3. 创建实体类
  访问 小木箱 github 仓库,通过 get 请求得到了以下报文:

 然后,通过 Gsonformat 得到相关实体类对象:
class MicroKibacoRepo {

   private int id;
   private String node_id;
   private String name;
   private String full_name;

// ---为了避免浪费篇幅,省略无用代码---
}
4. 创建⼀一个 interface 作为 Web Service 的请求集合,在⾥里⾯⽤注解(Annotation)写⼊需要配置的请求方法
public interface GitHubService {
 @GET("users/{user}/repos")
 Call> listRepos(@Path("user") String user);
}

5. 在正式代码里⽤ Retrofit 创建出 interface 的实例
  Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

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

6. 调⽤创建出的 Service 实例的对应⽅法,创建出相应的可以⽤来发起网络请求的 Call 对象
 Call> repos = service.listRepos("MicroKibaco");
7. 使用 Call.execute() 或者 Call.enqueue() 来发起请求
    repos.enqueue(new Callback>() {
            @Override
            public void onResponse(Call> call, Response> response) {

            }

            @Override
            public void onFailure(Call> call, Throwable t) {

            }
        });

三. Retrofit 常用 API

方法

作用

serviceMethodCache        

自定义的接口映射对象集合

baseUrl    

请求地址

callFactory    

默认为 OKHttpCall
converterFactories     数据解析器工厂集合

callbackExecutor  

回调执行,Android 平台默认为 MainThreadExecutor
Platform  Retrofit 中用来管理多平台的方法,支持 Android、Java8。通过 findPlatform 获取对应的平台,同时也初始化了 defaultCallAdapterFactory 工厂

ServiceMethod   

接口映射的网络请求对象,通过动态代理,将自定义接口的标注转换为该对象,将标注及参数生成 OkHttp 所需的 Request 对象。Retrofit 的 create 通过动态代理拦截,将每一个自定义接口转换成为一个 ServiceMethod 对象,并通过通过 serviceMethodCache 进行缓存
ExecutorCallAdapterFactory 回调执行,Android 平台默认为 MainThreadExecutor

CallAdapter.Factory    

CallAdapter 的静态工厂,包含 get 的抽象方法,用于生产 CallAdapter 对象
OkHttpCall  OkHttpCall 的 Call 实现,通过 createRawCall 得到真正的 okhttp3.Call 对象,用于进行实际的网络请求

Call  

 Retrofit 定义的网络请求接口,包含 execute、enqueue 等方法
Converter.Factory     数据解析器工厂,用于生产 Converter 实例

ExecutorCallAdapterFactory  

 

Android 平台默认的 CallAdapter 工厂,get 方法使用匿名内部类实现 CallAdapter,返回 ExecutorCallbackCall,实现了 CallExecutorCallbackCall
ExecutorCallbackCall   采用静态代理设计,delegate 实际为 OkHttpCall,使用 callbackExecutor 实现回调在主线程中执行

RxJavaCallAdapterFactory  

 Rxjava 平台的 CallAdapter 工厂,get 方法返回 RxJavaCallAdapter 对象
RxJavaCallAdapter  Rxjava 平台的设配器,返回 observable 对象

GsonConverterFactory    

数据解析工厂实例,返回了 GsonResponseBodyConverter 数据解析器
GsonResponseBodyConverter Gson 的数据解析器,将服务端返回的 json 对象转换成对应的 java 模型


四.源码解析
读源码如何开始呢?

我们先带着问题看源码,Retrofit 有几个关键的流程

  • Retrofit 如何将定义的 interface 转换成网络请求?
  • Retrofit 的 Converter 机制是如何实现?
  • Retrofit 的 CallAdapter 机制是如何实现?

1. 寻找入口
  一行一行读,肯定是不可行的,太累了,而且脑容量不够,记不住,合适的读源码方式是从程序入口开始入手。当前功能开始读。

   当前请求的入口发生在enqueue, 那么,我们就开始从这个 Api 入手读源码.

enqueue, 是进入队列的意思,进去查看,竟然是个

  void enqueue(Callback callback);
如果出现类似抽象方法;就得往回看了,因为读不下去了

2. 寻找接口实现
所以我们往上面找,上面的 Api 是 enqueue,去找 相关接口实现,找了半天,爱哭,接口实现是 GithubService

public interface GitHubService {
  @GET("users/{user}/repos")
  Call> listRepos(@Path("user") String user);
}
而 GithubService.java 是我自己创建的,怎么办呢? 继续往回看,找呀找, 找到了 Retrofit 的初始化方法 create,整个源码我就不翻出来了,翻一翻关键的部分,其中需要讲述的 APi 有:

  • eagerlyValidateMethods
  • newProxyInstance
  • Platform
  • invoke
  • invokeDefaultMethod

  public T create(final Class service) {

    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T)

    // --------动态代理--------
    Proxy.newProxyInstance(
    //  提供动态类的实例
    service.getClassLoader(),
    // 获取 GithubService 的实例,获取需要动态代理类的接口,在 retrofit.create 传入
    new Class[] { service },
    //
        new InvocationHandler() {
         // 判断接口平台,是JDK 7.0/8.0 或者 Android/IOS
          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 (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
             // java 8 开始,接口运行有默认的方法实现了
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

    static void validateServiceInterface(Class service) {
     // 1. 验证 GitHubService 是否为一个接口
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
     // 2.验证 GitHubService 是否继承其他接口,只接受原生接口
    if (service.getInterfaces().length > 0) {
      throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
    }
  }

  // 3. 在被初始化的时候,是有一个初始化过程,会去读注解,中间是有耗时的,分布式加载网络请求, validateEagerly 是方便调试的
    private void eagerlyValidateMethods(Class service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }

Platform.java 看一下相关实现,里面包含两个平台一个是 Java8 还有一个 Android,这个 APi 主要是平台标识,知道就行了,没什么好说的

    private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
      // Android 平台
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
       // Java8 平台
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }


loadServiceMethod 里面也没有 invoke,我们通过 parseAnnotations 去找 一下 invoke 试试看能否找到,

  ServiceMethod loadServiceMethod(Method method) {
    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.java里面,不过也是一个抽象方法 ,于是我们往回看 parseAnnotations,看里面是否有 invoke 的逻辑实现,找到了,好家伙,parseAnnotations 是 ServiceMethod 子类 HttpServiceMethod 实现方法,而 HttpServiceMethod 一定有 invoke 实现

abstract class ServiceMethod {
  static ServiceMethod 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.");
    }
// 通过 parseAnnotations 去寻找 invoke实现
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract T invoke(Object[] args);
}

不用说,我直接在 HttpServiceMethod.java, ctr + f 搜,便搜到了invoke,原来底层真是通过 OKHttp 实现的:

final class HttpServiceMethod extends ServiceMethod {
 // ----为了避免浪费篇幅,省略若干无用代码----
 @Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }
}

上面我说过,看到抽象方法,往回看 ,所以我们来看 okHttpCall,OKHttpCall 实现了 Call 接口 ,重点说一下一下方法:

createRawCall
 // OKhttp3的Call帮助Retrofit实现网络请求的Call
  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
1
call.enqueue
     call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {

      // response 结果交给 Retrofit 使用
        Response response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

什么是动态代理?

  动态代理是创建一个对象,这个对象传入一个接口并且帮你实现每一个接口的每一个方法,并且这每一个接口的每一个方法都会指向每一个接口每一个方法的 invoke 方法。


callAdapter

HttpServiceMethod.java里面的构造方法, 我们找到了 CallAdapter,然后去里面查看是通过 createCallAdapter返回的

  private static CallAdapter createCallAdapter(
      Retrofit retrofit, Method method) {
    Type returnType = method.getGenericReturnType();
    Annotation[] annotations = method.getAnnotations();
    try {
      // CallAdapter 创建
      return (CallAdapter) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
  }

进入 Retrofit.java 查看一下:

  public CallAdapter callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

    public CallAdapter nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {

 // 通过 callAdapterFactories 获取 callAdapter

    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      CallAdapter adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
 // ----为了避免浪费篇幅,省略若干无用代码----
  }

callAdaperFactory是通过 Retrofit 的 build 方法构建而来.

  public Retrofit build() {

      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

我们看到 callAdaperFactory 通过 addAll 一次性添加过来,所以有我们看一下defaultCallAdapterFactories方法.


    @Override List 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);
    }

然后我们再看一下 ExecutorCallAdapterFactory

五. 面试题分享
Retrofit 中的设计模式
1. 建造者模式
Retrofit 对象的创建、ServiceMethod 对象创建都使用 Build 模式,将复杂对象的创建和表示分离,调用者不需要知道复杂的创建过程,使用 Build 的相关方法进行配置创建对象。

2. 外观模式
Retrofit 对外提供了统一的调度,屏蔽了内部的实现,使得使用该网络库简单便捷。 门面模式: 提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。使用子系统更容易使用 

3. 动态代理模式
通过动态代理的方式,当调用 Retrofit 的 create()方法时,会进行动态代理监听。当执行具体的接口方法时,会回调 InvocationHandler。通过反射解析 method 的标注及参数,生成 ServiceMethod 对象。

4. 静态代理模式
Android 平台默认的适配器 ExecutorCallbackCall,采用静态代理的模式。具体的实现 delegate 为 OkHttpCall。

5. 工厂模式
Converter 及 CallAdapter 的创建都采用了工厂模式进行创建。

6. 适配器模式
CallAdapter 的 adapt 采用了适配器模式,使得 interface 的返回对象可以动态扩展,增强了灵活性

CallAdapter 的种类

Converter 种类
Retrofit 支持多种数据解析方式,使用时需要在 Gradle 添加依赖。


Retrofit 流程总结
————————————————
版权声明:本文为CSDN博主「小木箱」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Kibaco/article/details/109685912

你可能感兴趣的:(android,retrofit)