一、Retrofit 简介
Retrofit 是一个由 Square 出品的类型安全的 HTTP 网络请求库。它能够将 HTTP API 映射为接口,并以注解的方式定义请求方法和路径。开发者只需要定义接口,Retrofit 会负责发送网络请求、解析响应数据,并返回相应的对象。
二、添加依赖
在开始使用 Retrofit 之前,我们需要在项目的 build.gradle
文件中添加相关依赖。一般来说,Retrofit 通常搭配以下几个依赖使用:
- Retrofit 主库
- Retrofit 的转换器,例如
GsonConverterFactory
(用于将 JSON 数据转换为 Java/Kotlin 对象) - (可选)OkHttp(Retrofit 底层使用的 HTTP 客户端)
例如,在 app/build.gradle
文件中添加如下依赖:
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// 另外如果需要日志拦截器
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
}
先来插个API的概念:(你可以先sync一下上面的来看这个)
API 是“应用程序编程接口”(Application Programming Interface)的缩写。简单来说,API 是一组规则和协议,这些规则和协议定义了不同软件组件之间如何相互交互和通信。下面我们从几个角度来详细说明 API 的含义和作用:
-
定义功能:
API 规定了软件、服务或库可以提供哪些功能以及如何调用这些功能。开发者可以通过调用这些预定义好的接口来利用现有的功能,而不需要关心内部实现的细节。 -
软件组件之间的通信:
在大型软件系统中,不同模块、库或者服务经常需要协同工作。API 就像一个桥梁,使得各个部分可以使用统一的方式进行数据交换和功能调用。例如,当你调用一个网络 API(比如 RESTful API)时,请求被发送到服务器,服务器按照 API 的约定返回数据给客户端,客户端再根据这些数据进行后续处理。 -
隐藏实现细节:
API 提供了抽象的接口,屏蔽了具体的实现细节。使用者不必了解如何实现该功能,只需要按照 API 提供的规范调用接口即可。这提高了系统的模块化和安全性,同时也使得代码更容易维护和扩展。 -
常见的 API 类型:
- Web API(网络 API):通过 HTTP 协议调用的接口,如 RESTful API、GraphQL 等,常用于前后端分离的应用。
- 操作系统 API:操作系统为应用程序提供的接口,如文件系统、网络、硬件操作接口等。
- 库或框架的 API:如 Android SDK、Java API、Retrofit 的接口等,帮助开发者更方便地利用现有的功能库进行开发。
-
举例说明:
比如,在 Android 开发中,你可能会使用 Retrofit 库来请求网络数据。在这种情况下,Retrofit 定义了如何通过注解来描述 HTTP 请求 (例如 GET、POST 方法等),然后根据你的接口定义自动生成实现代码。你只需要关注 API 的调用,而无需手动处理底层的 HTTP 通信逻辑。
三、创建 API 接口
Retrofit 采用接口的方式来定义 API。例如,假设我们请求一个用户信息接口 GET /users/{id}
,接口可能返回一个 JSON 数据,我们可以定义如下数据模型和接口方法:
User.kt:
package com.example.myapplication
/*
它的作用是用来表示用户的信息
并且 Kotlin 编译器会自动为它生成一些常用的方法,例如 equals(), hashCode(), toString(), copy() 等。
在 Retrofit 中的作用:
在 Retrofit 中,User 类通常用于表示从 API 接口返回的 JSON 数据
当 Retrofit 接收到服务器返回的 JSON 数据时,它会使用 Gson 或其他 JSON 转换器将 JSON 数据自动转换为 User 对象。
*/
data class User(
val id: Int,
val name: String,
val email: String
)
下面是定义 API 接口的代码示例:
package com.example.myapplication
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Path
/*
接口使用 Retrofit 库定义了一个用于获取用户信息的 API 接口
它通过注解的方式声明了一个 HTTP GET 请求,并指定了请求路径和参数
该接口定义了一个 getUser 方法,该方法接收一个用户 ID 作为参数,并返回一个 Call<User> 对象,用于发起网络请求并获取用户信息。
接口是一种抽象类型,用于声明一组方法,但不提供方法的具体实现。
作用:定义一个 API 接口,用于声明网络请求方法。
@GET("users/{id}"): 这是一个注解,用于声明一个 HTTP GET 请求。
@GET: 表示这是一个 GET 请求。
"users/{id}": 表示请求的 URL 路径。 {id} 是一个占位符,用于表示用户 ID。
作用:指定 getUser 方法发起的是一个 HTTP GET 请求,请求路径为 "users/{id}"。
@Path("id"): 这是一个注解,用于将方法参数的值替换到 URL 路径中的占位符。
Call<User>: 表示方法的返回值类型为 Call<User>
Call<User> 是 Retrofit 库中的一个接口,用于表示一个待执行的网络请求,并指定响应数据的类型为 User。
*/
interface MyApiService {
// 定义一个获取用户信息的 GET 请求,{id} 表示动态参数
@GET("users/{id}")
//作用:定义一个 getUser 方法,该方法接收一个 userId 参数,用于替换 URL 路径中的 {id} 占位符
// 并返回一个 Call<User> 对象,用于发起网络请求并获取 User 信息。
fun getUser(@Path("id") userId: Int): Call<User>
}
/*
流程:
使用 Retrofit 库创建一个 MyApiService 接口的实现类。
调用 getUser 方法,并传入一个用户 ID。
Retrofit 库会将用户 ID 替换到 URL 路径中的 {id} 占位符,生成完整的 URL。
Retrofit 库会发起一个 HTTP GET 请求,请求该 URL。
服务器会返回一个包含用户信息的 JSON 数据。
Retrofit 库会将 JSON 数据转换为一个 User 对象。
getUser 方法会返回一个 Call<User> 对象,该对象包含了 User 对象。
你可以通过调用 Call<User> 对象的 enqueue 方法,异步地执行网络请求,并在回调函数中处理响应数据。
调用 getUser 方法,并传入一个用户 ID。 传用户ID是干什么?
指定请求目标
getUser(userId: Int) 方法中的 userId 参数用于指定要获取哪个用户的信息。
这个 userId 会被 @Path("id") 注解绑定到 URL 路径中的 {id} 占位符。
例如,如果你调用 getUser(123)
那么 Retrofit 实际请求的 URL 就会是 https://api.example.com/users/123(假设 BASE_URL 为 https://api.example.com/)。
RESTful API 设计原则
RESTful API 倾向于使用 URL 来标识资源。
在获取单个资源时,通常会将资源的 ID 放在 URL 路径中。
通过这种方式,服务器可以根据 URL 中的 ID 找到对应的用户数据,并将其返回给客户端。
服务器端的处理
当服务器接收到 https://api.example.com/users/123 这个请求时,它会解析 URL 路径中的 123 这个 ID。
服务器会根据这个 ID 从数据库或其他数据源中查询对应的用户信息。
如果找到了对应的用户,服务器会将用户信息封装成 JSON 格式的数据,并将其返回给客户端。
如果找不到对应的用户,服务器可能会返回一个 404 Not Found 错误。
"HTTP GET 请求" 是指客户端(例如你的 Android 应用)向服务器请求特定资源的一种方式,它基于 HTTP 协议。
1. HTTP 方法:GET
GET 是 HTTP 协议中定义的一种请求方法(也称为动词)。
GET 方法用于从服务器请求指定的资源。
GET 请求通常用于获取数据,而不是修改数据。
2. 请求过程
客户端创建一个 HTTP GET 请求,其中包含要请求的资源的 URL。
客户端将请求发送到服务器。
服务器接收到请求后,会根据 URL 查找对应的资源。
如果找到了资源,服务器会将资源的内容封装到 HTTP 响应中,并将其发送回客户端。
如果找不到资源,服务器会返回一个错误响应(例如 404 Not Found)。
Call<User>
1. Call 接口
Call 是 Retrofit 库中的一个核心接口。
它代表一个可以被执行的 HTTP 请求。
Call 接口定义了一些方法,例如 execute()(同步执行请求)和 enqueue()(异步执行请求),用于发起网络请求并获取响应数据。
2. 泛型 <User>
Call<User> 是一个泛型类型。
User 是泛型参数,表示响应数据的类型。
这意味着 Retrofit 会尝试将服务器返回的数据转换为 User 对象。
如果服务器返回的数据无法转换为 User 对象,Retrofit 会抛出一个异常。
3. 作用
Call<User> 对象封装了网络请求的所有信息,包括请求的 URL、HTTP 方法、请求头、请求体等。
你可以通过调用 Call<User> 对象的 execute() 或 enqueue() 方法来发起网络请求。
当网络请求成功时,Retrofit 会将服务器返回的数据转换为 User 对象,并将其传递给你的回调函数。
当网络请求失败时,Retrofit 会将错误信息传递给你的回调函数。
泛型(Generics)是一种编程技术,允许你在定义类、接口和方法时使用类型参数,而不是具体的类型
这些类型参数在使用时才被指定为实际的类型。 这样可以编写更通用、更灵活、类型安全的代码。
*/
在这个接口中:
@GET("users/{id}")
定义了一个 GET 请求,请求路径中{id}
会被@Path("id")
注解的参数替换。fun getUser(@Path("id") userId: Int): Call<User>
表示获取到的数据将会被反序列化成一个User
对象,并封装在Call<User>
中。
四、构建 Retrofit 实例
我们需要构建一个 Retrofit 实例,并创建 API 接口对象。通常我们会做一些全局配置,例如:设置基础 URL 以及注册 JSON 解析转换器。
package com.example.myapplication
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
object RetrofitClient {
private const val BASE_URL = "https://api.example.com/" // 注意最后的斜杠
// 可选:添加日志拦截器打印请求日志,便于调试
/*
HttpLoggingInterceptor() 是 OkHttp 库中提供的一个拦截器,用于记录 HTTP 请求和响应的详细日志。
当应用发出 HTTP 请求或接收响应时,HttpLoggingInterceptor 会截取这些交互的信息。
它可以记录请求行、请求头、响应状态码、响应头以及响应体内容。
对于调试网络请求、分析问题(例如请求格式不正确、服务器返回错误等)非常有帮助。
HttpLoggingInterceptor 提供几种日志记录级别,可以根据需要进行配置:
NONE:不记录任何信息。
BASIC:只记录请求和响应的摘要信息,例如请求方法、URL、响应状态码以及耗时。
HEADERS:在 BASIC 的基础上,还会记录请求和响应的头信息。
BODY:记录所有细节,包含请求和响应的全部信息(请求体和响应体)。
设置为 BODY 级别时,会输出所有数据,这在开发时调试非常有用,但在生产环境中一般不建议开启,以免泄露敏感信息。
HttpLoggingInterceptor.Level.BODY 是 OkHttp 日志拦截器中的一个日志级别配置
它表示记录所有 HTTP 请求与响应的细节信息,包括请求和响应的头部信息以及完整的消息体。
*/
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
/*
OkHttpClient.Builder() 是 OkHttp 库中用于构建 OkHttpClient 实例的一个构造器。
OkHttpClient 是 OkHttp 库中提供的一个类,用于处理 HTTP 网络请求
它封装了所有与网络通信相关的逻辑,包括连接管理、请求发送、响应接收、连接池管理和缓存支持等
通过 OkHttpClient,你可以方便地发起 GET、POST 等各种 HTTP 请求
并通过它内置的机制对请求进行配置和优化(例如添加拦截器、设置超时时间等)。
.addInterceptor(loggingInterceptor) 是 OkHttpClient.Builder 的一个方法调用
用于将一个拦截器(这里是 loggingInterceptor)添加到 OkHttpClient 实例中。
*/
private val httpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
// 构建 Retrofit 实例
/*
构建 Retrofit 实例的主要作用是创建一个用于网络通信的客户端
通过它可以向服务器发起 HTTP 请求,并将返回的数据转换为我们定义的数据模型
Retrofit 提供了一种简单、声明式的方式定义 API 接口,然后通过构建的 Retrofit 实例生成相应的实现,从而实现网络请求
.addConverterFactory(GsonConverterFactory.create()) 是 Retrofit 库中的一个方法调用
用于添加一个转换器工厂(Converter Factory),它负责将服务器返回的数据转换为应用程序可以使用的 Java 或 Kotlin 对象
具体来说,GsonConverterFactory.create() 创建了一个使用 Gson 库进行 JSON 数据转换的转换器工厂。
retrofit.create(MyApiService::class.java) 的作用是根据你定义的 API 接口生成一个实现类实例
使得你可以方便地发起网络请求并获取响应数据。
*/
val instance: MyApiService by lazy {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(MyApiService::class.java)
}
}
上面的代码中,我们:
- 定义了一个
RetrofitClient
对象,里面配置了基础 URL、OkHttp 客户端和 Gson JSON 转换器; - 使用
Retrofit.Builder()
构建 Retrofit 实例,并调用create()
方法生成MyApiService
接口的实现。
五、调用 API 接口
在 Activity 或其他类中调用 API 接口,通过 Retrofit 进行网络请求。Retrofit 提供两种方式:同步调用(execute()
)和异步调用(enqueue()
)。下面我们展示如何在 Android 中使用异步调用并在主线程中更新 UI。
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.IOException
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 假设当前布局文件为 activity_main.xml
setContentView(R.layout.activity_main)
// 示例:请求 id 为 1 的用户信息
/*
.enqueue(): 是 Call 接口的一个方法,用于异步执行网络请求。异步请求不会阻塞主线程(UI 线程),避免应用卡顿。
object : Callback<User> { ... }: 创建一个匿名对象,实现 Callback<User> 接口
Callback 接口用于处理异步请求的结果。User 是期望的响应数据类型。
*/
RetrofitClient.instance.getUser(1).enqueue(object : Callback<User> {
override fun onResponse(call: Call<User>, response: Response<User>) {
if (response.isSuccessful) {
// 获取服务器返回的 User 对象数据
response.body()?.let { user ->
// 在主线程中更新 UI
runOnUiThread {
Toast.makeText(
this@MainActivity,
"请求成功:${user.name}",
Toast.LENGTH_LONG
).show()
}
Log.d("MainActivity", "User Info: $user")
} ?: run {
// 如果 response.body() 返回 null,则说明响应内容为空
onFailure(call, IOException("Empty response body"))
}
} else {
// 响应不成功,可根据 response.errorBody() 做进一步处理
onFailure(call, IOException("Error response: ${response.code()}"))
}
}
override fun onFailure(call: Call<User>, t: Throwable) {
// 请求失败。这里可以在主线程中操作UI,比如弹.Toast消息
runOnUiThread {
Toast.makeText(
this@MainActivity,
"请求失败:${t.message}",
Toast.LENGTH_LONG
).show()
}
Log.d("MainActivity", "Error: ${t.message}")
}
})
}
}
在上面的例子中,我们:
- 调用
RetrofitClient.instance.getUser(1)
获取一个Call<User>
对象; - 使用
enqueue()
异步发送请求,并设置onResponse
和onFailure
两个回调。 - 在
onResponse
方法中,检查请求是否成功,若成功则通过response.body()
获取到User
对象,并更新 UI(记得需要切换到主线程更新 UI,使用runOnUiThread
)。 - 在
onFailure
方法中,处理请求失败,比如显示错误提示或进行日志记录。
六、总结
通过上面的示例,我们了解了 Retrofit 的基本用法,主要包括:
- 添加 Retrofit 和相关依赖。
- 定义数据模型和 API 接口,用注解标明 HTTP 请求方法及参数。
- 构建 Retrofit 实例,并配置基础 URL、OkHttp 客户端和转换器。
- 调用 API 接口进行网络请求,并通过异步回调处理请求结果。
- 在请求成功时更新 UI、处理响应数据,在请求失败时进行错误处理。