Retrofit 基本原理

幻昼 2024年04月29日 25次浏览

Retrofit 创建与配置过程

retrofit 简介

Retrofit 是一款基于 OkHttp 的强大网络请求库,专门用于简化 Android 和 Java 应用中的网络请求操作。它的设计理念是将 HTTP API 转换为 Java 接口,让开发者能够以更简洁、直观的方式与远程服务器进行通信,而无需直接处理底层的 HTTP 请求。

单 okhttp 的用法

private fun testOkhttp() {
    val okHttpClient = OkHttpClient.Builder()
        .readTimeout(5, TimeUnit.SECONDS)
        .build()

    val request = Request.Builder()
        .url("https://system.xlxs.top/config/getCameraSyncNotice?language=zh_CN")
        .get().build()

    val call = okHttpClient.newCall(request)
    call.enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
        }

        override fun onResponse(call: Call, response: Response) {
            Log.i("TAG-jin", "onResponse: ${response.body?.string()}")
        }

    })
}

使用 retrofit 的用法

kotlin 协程方式

先定义 service 接口,方法定义为挂起函数、再定义 协程 scope

interface MineService {

    // 定义一个挂起函数,用于获取相机同步通知
    @GET("config/getCameraSyncNotice")
    suspend fun getCameraSyncNotice(@Query("language") language: String): String

}

// 测试 Retrofit 的函数
private fun testRetrofit() {

    // 创建 Retrofit 实例
    val retrofit = Retrofit.Builder()
        .baseUrl("https://xxx.test.top/")
        .addConverterFactory(CustomGsonConverterFactory.create()) // 添加自定义的 Gson 转换器
        .build()
    
    // 创建 MineService 的实例
    val mineService = retrofit.create(MineService::class.java)

    // 使用 IO 线程发起网络请求,并在 Activity 生命周期内管理协程
    IOLifecycleScope(this).launch {
        try {
            // 调用 MineService 的函数获取相机同步通知
            val cameraSyncNotice = mineService.getCameraSyncNotice("")
            println(cameraSyncNotice) // 打印相机同步通知
        } catch (e: Exception) {
            e.printStackTrace() // 打印异常信息
        }
    }
}

// 在 Activity 生命周期内管理 IO 协程的作用域
class IOLifecycleScope(private val activity: AppCompatActivity) : CoroutineScope {
    private val job = SupervisorJob()

    // 定义协程上下文,指定为 IO 线程,并绑定到 SupervisorJob
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job

    // 在 Activity 销毁时取消协程
    init {
        activity.lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onDestroy(owner: LifecycleOwner) {
                cancel() // 取消协程
            }
        })
    }
}

传统方式

其中 Call 通过 CallAdapterFactory 来创建的,实例一般为 ExecutorCallbackCall

public interface ApiService {
    @GET("user/{id}")
    Call<User> getUser(@Path("id") int userId);

    @POST("user/create")
    Call<User> createUser(@Body User user);

    // 其他方法定义...
}

// 创建 Retrofit 实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// 创建接口实例
ApiService apiService = retrofit.create(ApiService.class);

// 发起请求
Call<User> call = apiService.getUser(1);
call.enqueue(new Callback<User>() {
    @Override
    public void onResponse(Call<User> call, Response<User> response) {
        // 处理响应结果
    }

    @Override
    public void onFailure(Call<User> call, Throwable t) {
        // 处理请求失败
    }
});

Retrofit 请求流程

动态代理生成接口的实现类基本流程

  • 动态生成接口的实现类: 当调用 Retrofit.create() 方法时,Retrofit 将使用动态代理机制来生成接口的实现类。具体流程如下:
    • Retrofit 使用 Java 的 java.lang.reflect.Proxy 类创建一个动态代理对象,该代理对象实现了接口定义的所有方法。
    • 在动态代理对象的方法调用中,会触发 InvocationHandlerinvoke() 方法,该方法被动态代理类实现,用于处理方法调用。
    • Retrofit 的 Proxy 类会将方法调用委托给 Retrofit 内部的 ServiceMethod 对象来处理,该对象负责解析方法的注解信息,并构建对应的 HTTP 请求对象,返回 Call。

注解如何转换成请求参数、生成Retrofit Call 过程

在使用过程中调用 service 实现类的方法就返回一个 Call,这里主要有两个步骤

loadServiceMethod(method)

调用接口定义的方法时会调用到 Retrofit#create 创建的 InvocationHandler 的 invoke 方法

会先调用 loadServiceMethod(method) 返回 ServiceMethod (HttpServiceMethod)。

这一步同时构建好 RequestFactory,该类会在 retrofit Call enqueue 时生成 okhttp Request,去构建 okhttp 的 Call

loadServiceMethod(method);

ServiceMethod.parseAnnotations(this, method);

RequestFactory.parseAnnotations(retrofit, method);


构建 RequestFactory
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
  return new Builder(retrofit, method).build();
}

RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
    ……
}

最终调用到了 parseMethodAnnotation  方法

parseMethodAnnotation

用于解析接口方法上的注解信息,并根据不同的注解类型执行相应的处理逻辑。以下是对这个方法的总结:

  1. 首先判断注解的类型,根据不同的注解类型执行不同的处理逻辑。
  2. 对于 HTTP 请求方法的注解(@GET@POST@PUT@DELETE@PATCH@HEAD@OPTIONS@HTTP),调用 parseHttpMethodAndPath 方法解析请求方法和请求路径,并根据注解中是否指定有请求体来确定是否需要包含请求体。
  3. 对于 @Headers 注解,解析其中的请求头信息,并存储到 this.headers 字段中。
  4. 对于 @Multipart@FormUrlEncoded 注解,分别标识当前请求是否为多部分请求或表单编码请求,并进行相应的处理。
HttpServiceMethod 的 invoke

再调用 HttpServiceMethod 的 invoke 方法,通过适配器生成 具体的 Call

// HttpServiceMethod 的 invoke,这里先 new 一个 OkHttpCall,再通过适配器传入到最终 Call 里面,赋给 delegate 字段
@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

// CallAdapted 继承了 HttpServiceMethod,调用其 adapt 方法,ReturnT 为泛型,就是 Call
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  return callAdapter.adapt(call);
}

// 再调用 CallAdapter 的 adapt 方法返回一个 ExecutorCallbackCall
@Override
public Call<Object> adapt(Call<Object> call) {
  return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}

Call enqueue 过程

在 Retrofit 中,Call 是 Retrofit 提供的一个抽象类, 实际上是通过 OkHttp 来执行这些网络请求的

当 Retrofit Call (ExecutorCallbackCall)的 enqueue() 方法被调用时,会调用 OkHttpCall enqueue 方法,也就是Call 里面的 delegate 属性。并使用 OkHttp 的 callFactory.newCall() 方法创建一个 OkHttp 的 Call 对象,最终将请求转发给 OkHttp 进行执行

请求转换到 okhttp流程以及线程切换

流程图, okhttp 内部 enqueue 使用了线程池异步调用 ,不能更新UI 。故而 retrofit 接到响应再切主线程

Retrofit_thread

Retrofit converter 转化原理

  • 返回 stream (respondBody) 到 json (Bean) GsonResponseBodyConverter

    上一步 okhttp3.Callback() onResponse 时会调用 parseResponse 方法

    会解析出来 body,也就是Call 封装的 泛型T,真正的响应内容

    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
      
      	  T body = responseConverter.convert(catchingBody);
          return Response.success(body, rawResponse);
          ……
    }
    // 这里的 responseConverter 就是配置时传进去的 convertor,把 okhttp 返回的 流转换成对象