跳到主要内容

网络请求

现代 Android 应用通常需要与服务器进行数据交互。本章将介绍如何使用 OkHttp 和 Retrofit 进行网络请求,以及如何处理 JSON 数据。

网络权限

首先,在 AndroidManifest.xml 中添加网络权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />

<application ...>
...
</application>

</manifest>

OkHttp

OkHttp 是 Square 公司开发的高性能 HTTP 客户端,是 Android 网络请求的基础库。

添加依赖

dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0")
}

基本使用

private val client = OkHttpClient()

// 同步 GET 请求(在子线程执行)
fun syncGet(url: String): String {
val request = Request.Builder()
.url(url)
.build()

client.newCall(request).execute().use { response ->
return response.body?.string() ?: ""
}
}

// 异步 GET 请求
fun asyncGet(url: String, callback: (String?) -> Unit) {
val request = Request.Builder()
.url(url)
.build()

client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 请求失败
callback(null)
}

override fun onResponse(call: Call, response: Response) {
// 请求成功
val body = response.body?.string()
callback(body)
}
})
}

POST 请求

// POST JSON 数据
fun postJson(url: String, json: String): String {
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = json.toRequestBody(mediaType)

val request = Request.Builder()
.url(url)
.post(body)
.build()

client.newCall(request).execute().use { response ->
return response.body?.string() ?: ""
}
}

// POST 表单数据
fun postForm(url: String, params: Map<String, String>): String {
val formBody = FormBody.Builder().apply {
params.forEach { (key, value) ->
add(key, value)
}
}.build()

val request = Request.Builder()
.url(url)
.post(formBody)
.build()

client.newCall(request).execute().use { response ->
return response.body?.string() ?: ""
}
}

添加请求头

val request = Request.Builder()
.url(url)
.addHeader("Authorization", "Bearer $token")
.addHeader("Content-Type", "application/json")
.build()

拦截器

拦截器可以用于日志记录、添加公共请求头、缓存等:

// 日志拦截器
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

val client = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()

// 自定义拦截器(添加公共请求头)
class AuthInterceptor(private val token: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer $token")
.build()
return chain.proceed(request)
}
}

Retrofit

Retrofit 是 Square 公司开发的 REST 客户端,基于 OkHttp,提供了类型安全的 HTTP API 接口定义。

添加依赖

dependencies {
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.google.code.gson:gson:2.10.1")
}

定义 API 接口

// 数据类
data class User(
val id: Int,
val name: String,
val email: String
)

data class LoginRequest(
val username: String,
val password: String
)

data class LoginResponse(
val token: String,
val user: User
)

// API 接口
interface ApiService {

@GET("users")
suspend fun getUsers(): List<User>

@GET("users/{id}")
suspend fun getUserById(@Path("id") id: Int): User

@GET("users")
suspend fun searchUsers(@Query("name") name: String): List<User>

@POST("auth/login")
suspend fun login(@Body request: LoginRequest): LoginResponse

@POST("users")
suspend fun createUser(@Body user: User): User

@PUT("users/{id}")
suspend fun updateUser(@Path("id") id: Int, @Body user: User): User

@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") id: Int)

@GET("users")
suspend fun getUsersWithHeader(
@Header("Authorization") token: String
): List<User>

@FormUrlEncoded
@POST("auth/login")
suspend fun loginForm(
@Field("username") username: String,
@Field("password") password: String
): LoginResponse
}

创建 Retrofit 实例

object RetrofitClient {
private const val BASE_URL = "https://api.example.com/"

private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()

val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}

使用 API

class UserViewModel : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> = _users

private val _error = MutableLiveData<String>()
val error: LiveData<String> = _error

private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean> = _isLoading

fun loadUsers() {
viewModelScope.launch {
_isLoading.value = true
try {
val result = RetrofitClient.apiService.getUsers()
_users.value = result
} catch (e: Exception) {
_error.value = e.message ?: "加载失败"
} finally {
_isLoading.value = false
}
}
}

fun login(username: String, password: String) {
viewModelScope.launch {
_isLoading.value = true
try {
val request = LoginRequest(username, password)
val response = RetrofitClient.apiService.login(request)
// 保存 token,跳转页面
} catch (e: Exception) {
_error.value = e.message ?: "登录失败"
} finally {
_isLoading.value = false
}
}
}
}

在 Activity/Fragment 中使用

class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

// 观察数据
viewModel.users.observe(this) { users ->
// 更新 UI
binding.recyclerView.adapter = UserAdapter(users)
}

viewModel.error.observe(this) { error ->
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
}

viewModel.isLoading.observe(this) { isLoading ->
binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
}

// 加载数据
viewModel.loadUsers()
}
}

错误处理

异常类型

sealed class NetworkResult<out T> {
data class Success<out T>(val data: T) : NetworkResult<T>()
data class Error(val message: String) : NetworkResult<Nothing>()
data object Loading : NetworkResult<Nothing>()
}

suspend fun <T> safeApiCall(apiCall: suspend () -> T): NetworkResult<T> {
return try {
NetworkResult.Success(apiCall())
} catch (e: HttpException) {
NetworkResult.Error("网络错误: ${e.code()}")
} catch (e: IOException) {
NetworkResult.Error("网络连接失败")
} catch (e: Exception) {
NetworkResult.Error(e.message ?: "未知错误")
}
}

// 使用
fun loadUsers() {
viewModelScope.launch {
_result.value = NetworkResult.Loading
_result.value = safeApiCall {
RetrofitClient.apiService.getUsers()
}
}
}

响应状态码处理

fun handleResponse(response: Response<T>): NetworkResult<T> {
return when {
response.isSuccessful -> {
val body = response.body()
if (body != null) {
NetworkResult.Success(body)
} else {
NetworkResult.Error("响应体为空")
}
}
response.code() == 401 -> NetworkResult.Error("未授权,请登录")
response.code() == 403 -> NetworkResult.Error("禁止访问")
response.code() == 404 -> NetworkResult.Error("资源不存在")
response.code() >= 500 -> NetworkResult.Error("服务器错误")
else -> NetworkResult.Error("请求失败: ${response.code()}")
}
}

协程与网络请求

Retrofit 支持 Kotlin 协程,使网络请求代码更加简洁:

// 在 ViewModel 中使用
class MainViewModel : ViewModel() {

fun loadData() {
viewModelScope.launch {
// 并行请求多个接口
try {
val deferredUsers = async { RetrofitClient.apiService.getUsers() }
val deferredPosts = async { RetrofitClient.apiService.getPosts() }

val users = deferredUsers.await()
val posts = deferredPosts.await()

// 处理数据
} catch (e: Exception) {
// 处理错误
}
}
}
}

// 在 Repository 中使用
class UserRepository {
suspend fun getUsers(): List<User> {
return RetrofitClient.apiService.getUsers()
}

suspend fun getUserById(id: Int): User {
return RetrofitClient.apiService.getUserById(id)
}
}

小结

本章介绍了 Android 网络请求的核心内容:

  1. 网络权限:添加 INTERNET 权限
  2. OkHttp:基础 HTTP 客户端,支持同步/异步请求
  3. Retrofit:类型安全的 REST 客户端,推荐使用
  4. 错误处理:统一处理网络异常和响应状态
  5. 协程:简化异步网络请求代码

下一章将学习 Android 架构组件,包括 ViewModel、LiveData 等。