Retrofit 入门
本教程共 100 篇 · 第 70 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
70. Retrofit 入门
本节目标:把 Retrofit 跑起来,学会定义 API 接口、构造 Retrofit 实例、用
suspend方法发 GET/POST 请求,把 JSON 自动解析成 Kotlin 对象。
上一章讲了网络基础,知道 Android 联网要权限、要子线程、要 HTTPS。这一章直接上 Retrofit,这是目前 Android 网络开发的事实标准。
Retrofit 是什么
Retrofit 是 Square 公司出的类型安全 HTTP 客户端,底层是 OkHttp。
打个比方,OkHttp 像你自己手写 HTTP 报文发出去再收回来,每步都得管;Retrofit 像给你配了个翻译,你只管用 Kotlin 写接口方法,它帮你把方法调用翻译成 HTTP 请求,再把响应翻译成 Kotlin 对象。
它的好处:
- 类型安全:方法返回
User就是User,不用手动解析 JSON。 - 接口化:一个接口对应一组 API,方法上贴注解(
@GET、@POST)说明 HTTP 方法和路径。 - 编译期校验:URL 写错、参数对不上直接编译报错。
- 协程原生支持:方法加
suspend,自动切线程。
四步搭起来
第一步:加依赖
打开 app/build.gradle.kts:
dependencies {
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.11.0")
// Moshi 转换器(推荐,比 Gson 更适配 Kotlin)
implementation("com.squareup.retrofit2:converter-moshi:2.11.0")
implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
// 如果用 Gson 也行
// implementation("com.squareup.retrofit2:converter-gson:2.11.0")
}
加 INTERNET 权限到 AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
NoteMoshi 和 Gson 都能解析 JSON。Moshi 是 Square 出的,对 Kotlin 的可空类型、默认值支持更好,新项目建议用 Moshi。本教程两种都讲,主线用 Moshi。
第二步:定义数据类
后端返回的 JSON 长这样:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere",
"body": "quia et suscipit..."
}
对应 Kotlin 数据类:
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class Post(
val userId: Int,
val id: Int,
val title: String,
val body: String
)
字段名和 JSON 的 key 一致就行。不一致用 @Json(name = "...") 映射:
data class Post(
@Json(name = "user_id") val userId: Int, // JSON 里是 user_id
val id: Int,
val title: String,
val body: String
)
TipMoshi 的
@JsonClass(generateAdapter = true)会在编译期生成解析代码,比运行时反射快得多,还能减少启动开销。记得配 KSP 才能用。
第三步:定义 API 接口
接口里每个方法对应一个 HTTP 请求:
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
interface PostApi {
@GET("posts")
suspend fun listPosts(): List<Post>
@GET("posts/{id}")
suspend fun getPost(@Path("id") id: Int): Post
@GET("posts")
suspend fun getPostsByUser(@Query("userId") userId: Int): List<Post>
}
看几个细节:
@GET("posts")表示 GET 请求,路径拼在 baseUrl 后面。{id}是路径占位符,用@Path注入实际值。@Query拼到 URL 查询参数上,?userId=1这样。- 方法是
suspend,Retrofit 自动切 IO 线程,调用方在协程里直接 await。
第四步:构造 Retrofit 实例
写个单例对象集中管理:
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
object RetrofitClient {
private const val BASE_URL = "https://jsonplaceholder.typicode.com/"
val api: PostApi by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.build()
.create(PostApi::class.java)
}
}
要点:
baseUrl必须以/结尾,否则报错。addConverterFactory装上 JSON 转换器,Moshi 或 Gson 选一个。create(PostApi::class.java)这一步把接口「实现」出来,返回的就是个能调用的对象。
WarningbaseUrl 路径和
@GET路径会拼接,规则容易搞混。简单记:baseUrl 以/结尾,@GET路径不要以/开头。https://api.com/+posts=https://api.com/posts,对。
发起请求
在协程里调用就行:
class PostRepository(private val api: PostApi = RetrofitClient.api) {
suspend fun fetchPost(id: Int): Post {
return api.getPost(id)
}
suspend fun fetchAll(): List<Post> {
return api.listPosts()
}
}
ViewModel 或 Compose 里用:
val scope = rememberCoroutineScope()
var post by remember { mutableStateOf<Post?>(null) }
var error by remember { mutableStateOf<String?>(null) }
Button(onClick = {
scope.launch {
try {
post = PostRepository().fetchPost(1)
} catch (e: Exception) {
error = e.message
}
}
}) {
Text("拉取")
}
post?.let { Text("标题:${it.title}") }
POST 请求
POST 一般带请求体,用 @Body 注入对象:
import retrofit2.http.Body
import retrofit2.http.POST
interface PostApi {
@POST("posts")
suspend fun createPost(@Body post: Post): Post
}
调用:
suspend fun publishPost(title: String, body: String): Post {
val newPost = Post(
userId = 1,
id = 0, // 后端会分配
title = title,
body = body
)
return api.createPost(newPost)
}
后端返回创建好的资源,自动解析成 Post 对象给你。
表单和文件上传
表单提交用 @FormUrlEncoded:
import retrofit2.http.FormUrlEncoded
import retrofit2.http.Field
interface AuthApi {
@FormUrlEncoded
@POST("login")
suspend fun login(
@Field("username") username: String,
@Field("password") password: String
): LoginResponse
}
文件上传用 @Multipart:
import retrofit2.http.Multipart
import retrofit2.http.Part
import okhttp3.MultipartBody
interface UploadApi {
@Multipart
@POST("upload")
suspend fun uploadImage(
@Part image: MultipartBody.Part
): UploadResponse
}
构造 MultipartBody.Part:
val file = File(cacheDir, "photo.jpg")
val requestFile = file.asRequestBody("image/jpeg".toMediaType())
val multipart = MultipartBody.Part.createFormData("image", file.name, requestFile)
api.uploadImage(multipart)
下一章进阶会展开讲拦截器和错误处理。
处理响应
直接返回对象用着舒服,但拿不到 HTTP 状态码。要拿原始响应,包一层 Response<T>:
@GET("posts/{id}")
suspend fun getPostRaw(@Path("id") id: Int): Response<Post>
调用:
val response = api.getPostRaw(1)
if (response.isSuccessful) {
val post = response.body() // 拿到 Post
println(post?.title)
} else {
val code = response.code() // 404, 500...
val errorBody = response.errorBody()?.string() // 错误响应体
}
Tip习惯做法:业务接口直接返回对象(让 Retrofit 不成功时抛异常),需要细判状态码的接口用
Response<T>,二者按场景选。
常见坑
- baseUrl 不以
/结尾:直接抛IllegalArgumentException。 - JSON 字段名对不上:返回 null 或者数据类构造失败。用
@Json(name = ...)映射。 suspend方法忘了写:调用api.getPost(1)不返回协程,会卡死或者编译报错。- JSON 字段可空但数据类写非空:解析失败抛异常。后端可能返回 null 的字段要写成
String?。 - Moshi 没配 KSP:
@JsonClass(generateAdapter = true)不生效,退回到反射模式,性能差。
小结
Retrofit 三步就能用:加依赖、定义接口、构造实例。接口方法用 suspend 配合协程,JSON 自动解析成 Kotlin 对象,省掉一大堆样板代码。GET 用 @GET + @Path / @Query,POST 用 @POST + @Body,表单和文件用 @FormUrlEncoded / @Multipart。
下一章讲拦截器、Header、错误处理、超时配置这些进阶话题。