Android Retrofit源码分析(一):Retrofit是什么?和OkHttp的区别是什么?为什么需要他?

659 阅读6分钟

目录

在这里插入图片描述


一、Retrofit是什么?

Retrofit是一个基于OKHttp的RESTful网络请求框架,由Square公司开源,专为Android和Java提供类型安全的HTTP客户端。它可以理解为OKHttp的加强版,底层封装了OKHttp,主要负责网络请求接口的封装,使得网络请求工作更加简洁高效。

简单来说,Retrofit是OkHttp的封装,但他没有任何的网络请求功能,他只是对请求前,请求后的数据过程进行封装,网络请求功能还是在OkHttp里面。


二、为什么会出现Retrofit?它的作用是什么?

2.1 OkHttp使用上的缺陷

以前我们使用OKHttp发起请求,需要创建request,指定域名等等,每次发送请求,都需要些一大堆的内容。不够简单,不够复用。比如下面的代码,每次发送请求,都需要我们写一次Request。并且请求回来的内容,我们都需要对Response进行解析转换。

val client = OkHttpClient()
		//1. 每次请求都需要创建request来指定url,参数等。
        val request = Request.Builder()
            .url(url)
            .build()

        var startTime = System.currentTimeMillis() 

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                val endTime = System.currentTimeMillis()
                val duration = endTime - startTime
                callback(null, duration) 
                e.printStackTrace()
            }

            override fun onResponse(call: Call, response: Response) {
                val endTime = System.currentTimeMillis()
                val duration = endTime - startTime

                if (response.isSuccessful) {
                    // 2. 读取响应体,并且是Response类型的,还需要进行解析,我们希望能够得到指定类型的bean。
                    response.body?.string()?.let { responseBody ->
                        callback(responseBody, duration) 
                    }
                } else {
                    callback(null, duration)
                    println("HTTP请求失败: ${response.code}")
                }
            }
        })

OKHttp设计非常好的,但是在使用方面,用户网络请求的接口配置繁琐,尤其是需要配置复杂请求body,请求头,参数的时候;以及数据解析过程需要用户手动拿到responsbody进行解析,不能复用;所以呢,如果使用OkHttp,我们需要在封装一次。不过,现在不用了,Retrofit正是来帮助我们解决这个问题。

在这里插入图片描述 我们在发起请求的时候,是操作Retrofit,所以它可以对我们的请求进行封装,并且OkHttp数据回来的时候,Retrofit又可以帮助我们解析,然后再将数据发送给我们。这就是Retrofit的作用,以及它使用再什么地方。

所以呢,网络请求的工作本质上是OkHttp完成,而Retrofit仅负责网络请求接口的封装。

App应用程序通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数、Header、Url等信息,之后由OkHttp完成后续的请求操作。 在服务端返回数据之后, OkHttp将原始的结果交给Retrofit, Retrofit根据用户的需求对结果进行解析。


三、那么它是如何去做这个优化的?

我们先从代码入手。如下是一个使用retrofit发起的get请求。

interface MyApi {  
    @GET("data") // 假设你的API端点是https://api.example.com/data  
    fun getData(  
        @Query("param1") param1: String,  
        @Query("param2") param2: String,  
        @Query("param3") param3: String? = null // param3是可选的,所以这里使用了Kotlin的可空类型  
    ): Call<MyResponse>  
}
import retrofit2.Retrofit  
import retrofit2.converter.gson.GsonConverterFactory  
  
// 假设你有一个全局的Retrofit实例,或者你可以在这个方法中创建它  
// 注意:通常你会想要重用Retrofit实例,而不是每次请求都创建一个新的  
val retrofit = Retrofit.Builder()  
    .baseUrl("https://api.example.com/")  
    .addConverterFactory(GsonConverterFactory.create())  
    .build()  
  
val myApi: MyApi = retrofit.create(MyApi::class.java)  
  
// 发起网络请求  
val call = myApi.getData("value1", "value2", "value3") // 或者对于可选参数,你可以传递null  
  
call.enqueue(object : retrofit2.Callback<MyResponse> {  
    override fun onResponse(call: Call<MyResponse>, response: retrofit2.Response<MyResponse>) {  
        if (response.isSuccessful) {  
            val myResponse = response.body()  
            // 处理响应数据  
            myResponse?.let {  
                // 在这里使用myResponse  
                println("Status: ${it.status}")  
                it.data?.let { data ->  
                    println("Data Field1: ${data.field1}")  
                    println("Data Field2: ${data.field2}")  
                }  
            }  
        } else {  
            // 处理错误情况  
            println("Error: ${response.errorBody()?.string()}")  
        }  
    }  
  
    override fun onFailure(call: Call<MyResponse>, t: Throwable) {  
        // 处理请求失败的情况  
        println("Request failed: $t")  
    }  
})

3.1 bulid

我们先这一部分开始讲起。

// 假设你有一个全局的Retrofit实例,或者你可以在这个方法中创建它  
// 注意:通常你会想要重用Retrofit实例,而不是每次请求都创建一个新的  
val retrofit = Retrofit.Builder()  
    .baseUrl("https://api.example.com/")  
    .addConverterFactory(GsonConverterFactory.create())  
    .build() 

建立一个Retrofit对象的标准,配置好Retrofit类里的成员变量,像baseUrl,response的数据转换器等等。方便我们后面进行组装request或解析response的时候使用。

1)baseUrl("api.example.com/") :方法用于设置所有网络请求的基础URL。这意味着,当你定义接口中的方法时,你只需要指定相对于这个基础URL的路径部分。Retrofit 会自动将基础URL和路径部分拼接起来形成完整的请求URL。

2).addConverterFactory(GsonConverterFactory.create()) 方法用于添加Gson转换器工厂。Gson是一个Google提供的Java库,用于将Java对象序列化为JSON字符串,以及将JSON字符串反序列化为Java对象。通过添加Gson转换器工厂,Retrofit 能够自动地将请求体(通常是Java对象)转换为JSON字符串发送给服务器,并将服务器返回的JSON字符串自动转换为Java对象。

3)最后,通过调用 .build() 方法,我们根据前面的配置构建了一个 Retrofit 实例。这个实例随后可以用来创建服务接口(Service Interface)的实例。

3.2 create

val myApi: MyApi = retrofit.create(MyApi::class.java)  

Retrofit会分析你传入的接口(MyApi),查找并解析所有使用Retrofit注解标记的方法。这些注解提供了关于如何执行HTTP请求的必要信息。

基于接口的分析结果,Retrofit会使用Java的动态代理机制生成一个实现了MyApi接口的代理对象。这个代理对象会在你调用接口中的方法时,自动地构建并执行相应的HTTP请求。

点击create方法进去源码 在这里插入图片描述 可以看到,create 方法通过newProxyInstance方法创建一个实现了MyApi接口的代理对象,因为接口不能直接调用方法。所以它创建了一个代理,可以看到使用的时候动态代理。动态,就是运行时的意思。在程序运行过程中创建了一个对象。 在这里插入图片描述 我们可以看到它返回一个对象,使用classLoader类加载器来生成一个对象,返回的对象就是实现了这个接口的对象。

(1)invoke方法

@Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // 如果是Object类的方法(如toString(), hashCode(), equals()等),那么不做特殊的处理。
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                //isDefaultMethod是判断接口的方法是否有默认的实现,如果有默认的实现,那么就执行默认的实现,如果没有则走loadServiceMethod,这个方法下面我们特殊讲解一下。
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });

为什么要使用invoke方法?也就是为什么要拦截?这样就可以拦截到接口的所有信息,注解、参数、返回值,就是用来构建request的动态变化内容。动态构建URL。

(2)loadServiceMethod

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;
  }

调用loadServiceMethod方法构建一个ServiceMethod,这里使用了缓存,为什么使用缓存?因为ServiceMethod里面使用了反射,我们都知道反射是比较消耗性能的,所以他们创建以后,就使用一个map集合来存储起来。 在这里插入图片描述 那么ServiceMethod究竟是做什么?

3.3 ServiceMethod

ServiceMethod是什么?它主要是:

  1. 注解解析:通过Java反射机制解析接口方法上的注解(如@GET、@POST等),根据这些注解生成OkHttp所需的Request参数。
  2. 创建请求:基于解析得到的注解信息和方法的参数,ServiceMethod负责构造完整的HTTP请求。这包括构建请求的URL(可能包含路径变量和查询参数)、设置请求头、以及准备请求体(对于POST、PUT等请求方法)。

这几点,我们可以在ServiceMethod里面看到。

在这里插入图片描述 在这里插入图片描述 现在 ServiceMethod 创建好了,我们要用他来构造一个 OkHttpCall。 在这里插入图片描述 OkHttpCall是什么?我们知道,如果要发起请求,那么就一定要调用OkHttp的call,然后调用call的enqueue方法发请求。所以OkHttpCall是Retrofit中对OkHttp中Call接口的封装。它实现了Retrofit的Call接口,并内部持有一个OkHttp的Call对象。

为什么要创建一个OkHttpCall?这样做的好处是,Retrofit可以在不直接依赖OkHttp具体实现的情况下,提供一套自己的网络请求API,从而增加了代码的抽象层次和灵活性。

OkHttpCall在请求发送前会负责填充请求所需的所有信息,包括URL、请求头、请求体等。在收到响应后,它还会负责处理响应数据,将其转换为开发者期望的格式(如Java对象)。

这篇文章就介绍到这里。