客户端-服务器模型
在开始之前,理解基本模型很重要:

- 客户端: 你的 Android App,它负责向服务器发送请求,并接收和解析服务器返回的数据。
- 服务器: 运行在远程计算机上的应用程序,它接收客户端的请求,处理业务逻辑(如查询数据库),然后将数据以标准格式(通常是 JSON)返回给客户端。
- 通信协议: 客户端和服务器之间“对话”的语言,最常用的是 HTTP/HTTPS,HTTP 请求方法中,
GET用于获取数据,POST用于提交数据。
实现方式演进:从传统到现代
获取数据的方式随着 Android 开发的发展而不断演进,主要有以下几种方式:
- 传统方式:
HttpURLConnection(Java 原生) - 现代流行方式: Retrofit + OkHttp (行业标准)
- 现代便捷方式: Android Jetpack 的
ViewModel+LiveData/Flow
我们将重点讲解最推荐的 Retrofit 方式,并简要提及其他方式。
使用 Retrofit (强烈推荐)
Retrofit 是一个类型安全的 HTTP 客户端,由 Square 公司开发,它极大地简化了网络请求的代码,让你可以用简洁的 Java/Kotlin 接口来定义 API。
步骤 1:添加依赖
在 app/build.gradle.kts (或 build.gradle) 文件中添加 Retrofit 和 Gson (用于 JSON 解析) 的依赖。

// build.gradle.kts (Kotlin DSL)
dependencies {
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// Retrofit Gson 转换器
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// OkHttp 日志拦截器 (用于调试网络请求)
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
}
步骤 2:添加网络权限
在 app/src/main/AndroidManifest.xml 文件中添加网络访问权限。
<manifest ...>
<!-- 必须添加的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<application ...>
...
</application>
</manifest>
步骤 3:创建数据模型
根据服务器返回的 JSON 结构,创建对应的 Kotlin 数据类,Retrofit 会自动将 JSON 映射到这些类。
服务器返回的 JSON 如下:
[
{
"userId": 1,
"id": 1,: "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
...
]
你可以创建对应的 Post 数据类:

// Post.kt
data class Post(
val userId: Int,
val id: Int,
val title: String,
val body: String
)
步骤 4:创建 Retrofit API 接口
定义一个接口,用注解来描述 API 的端点、请求方法、路径和参数。
// ApiService.kt
import retrofit2.Call
import retrofit2.http.GET
interface ApiService {
// @GET 表示这是一个 GET 请求
// "posts" 是 API 的相对路径
// suspend 关键字使这个函数可以在协程中挂起,返回 List<Post>
@GET("posts")
suspend fun getPosts(): List<Post>
}
@GET: 指定 HTTP 请求方法为 GET。@Path("id"): 用于 URL 路径中的变量,如@GET("posts/{id}")。@Query("param"): 用于 URL 查询参数,如@GET("posts")可以搭配@Query("page") page: Int。@Body: 用于 POST 请求,发送一个对象作为请求体。suspend: 将函数标记为挂起函数,使其可以在协程中使用,这是现代 Android 开发的推荐做法。
步骤 5:创建 Retrofit 实例并构建 API 服务
// RetrofitClient.kt
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
private const val BASE_URL = "https://jsonplaceholder.typicode.com/" // 示例 API
val instance: ApiService by lazy {
// 1. 创建日志拦截器,方便调试
val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
// 2. 创建 OkHttp 客户端
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
// 3. 构建 Retrofit 实例
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) // 添加 Gson 转换器
.client(okHttpClient)
.build()
// 4. 创建 API 服务的实现
retrofit.create(ApiService::class.java)
}
}
步骤 6:在 ViewModel 或 Activity/Fragment 中调用 API
最佳实践:在 ViewModel 中调用
ViewModel 负责为 UI 提供数据,并且能在配置更改(如屏幕旋转)时存活下来,是处理网络请求的理想场所。
添加 lifecycle-viewmodel-ktx 依赖:
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
然后创建 ViewModel:
// MyViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class MyViewModel : ViewModel() {
// 使用 MutableLiveData 来存储和更新数据
private val _posts = MutableLiveData<List<Post>>()
val posts: LiveData<List<Post>> = _posts
// 错误信息
private val _error = MutableLiveData<String>()
val error: LiveData<String> = _error
init {
fetchPosts()
}
private fun fetchPosts() {
// viewModelScope.launch 会自动在 ViewModel 销毁时取消协程,防止内存泄漏
viewModelScope.launch {
try {
// 调用 Retrofit API
val response = RetrofitClient.instance.getPosts()
_posts.postValue(response) // 更新 LiveData
} catch (e: Exception) {
_error.postValue("获取数据失败: ${e.message}")
}
}
}
}
在 Activity/Fragment 中观察数据并更新 UI
// MyActivity.kt
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MyActivity : AppCompatActivity() {
// 使用 Ktx 库轻松获取 ViewModel 实例
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
val adapter = PostAdapter()
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
// 观察 LiveData 的变化
viewModel.posts.observe(this) { posts ->
posts?.let {
// 数据更新时,通知适配器
adapter.submitList(it)
}
}
// 观察错误信息
viewModel.error.observe(this) { errorMessage ->
errorMessage?.let {
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}
}
}
}
使用 Android Jetpack (ViewModel + Flow)
上面的示例已经结合了 ViewModel,现代开发中,我们更倾向于使用 Kotlin 的 Flow 来处理异步数据流,因为它能更优雅地处理背压和生命周期。
修改 ViewModel 以使用 Flow
你需要添加 lifecycle-runtime-ktx 依赖:
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
修改后的 ViewModel:
// MyViewModelWithFlow.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class MyViewModelWithFlow : ViewModel() {
// 使用 StateFlow 来持有状态,它具有生命周期感知能力
private val _posts = MutableStateFlow<List<Post>>(emptyList())
val posts: StateFlow<List<Post>> = _posts
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading
private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error
init {
fetchPosts()
}
private fun fetchPosts() {
viewModelScope.launch {
_isLoading.value = true
try {
val response = RetrofitClient.instance.getPosts()
_posts.value = response
_error.value = null // 清除错误
} catch (e: Exception) {
_error.value = "获取数据失败: ${e.message}"
} finally {
_isLoading.value = false
}
}
}
}
在 Activity/Fragment 中观察 StateFlow:
// MyActivityWithFlow.kt
// ... (其他代码相同)
viewModel.posts.observe(this) { posts ->
adapter.submitList(posts)
}
viewModel.isLoading.observe(this) { isLoading ->
// 显示或隐藏加载指示器
progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
}
viewModel.error.observe(this) { error ->
error?.let {
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}
}
传统方式 HttpURLConnection (不推荐用于新项目)
这种方式代码繁琐,需要手动处理线程、输入流和 JSON 解析,容易出错,仅作为了解。
// 在后台线程中执行
Thread {
try {
val url = URL("https://jsonplaceholder.typicode.com/posts")
val urlConnection = url.openConnection() as HttpURLConnection
urlConnection.requestMethod = "GET"
// 检查响应码
if (urlConnection.responseCode == HttpURLConnection.HTTP_OK) {
val inputStream = urlConnection.inputStream
val response = inputStream.bufferedReader().use { it.readText() }
// 1. 手动解析 JSON (可以使用 Gson 或 Moshi 库辅助)
// val posts = Gson().fromJson(response, Array<Post>::class.java)
// 2. 切换到主线程更新 UI
runOnUiThread {
// 将解析后的数据设置给 UI
textView.text = response.substring(0, 200) // 示例:只显示前200个字符
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
缺点:
- 必须手动创建后台线程。
- 代码冗长,易出错。
- 没有现代化的特性,如协程支持、类型安全等。
总结与最佳实践
| 特性 | Retrofit + ViewModel + Flow | HttpURLConnection |
|---|---|---|
| 代码简洁性 | 极高,接口定义清晰 | 低,代码冗长 |
| 类型安全 | 高,编译时检查 API | 低,字符串硬编码 |
| 异步处理 | 优雅,使用协程/Flow | 繁琐,需手动管理线程 |
| 可维护性 | 高,易于扩展和修改 | 低,耦合度高 |
| 生态系统 | 强大,与 OkHttp、Gson/Moshi 等无缝集成 | 弱,需自行整合工具 |
| 推荐度 | 强烈推荐 (现代 Android 开发标准) | 仅用于学习或极简单场景 |
最终推荐的工作流:
- 定义 API: 使用 Retrofit 接口清晰描述你的后端 API。
- 创建数据模型: 为每个 JSON 响应创建对应的 Kotlin 数据类。
- 构建网络层: 创建一个单例的 Retrofit 客户端。
- 处理数据逻辑:
- 在
ViewModel中,使用viewModelScope.launch启动一个协程。 - 在协程中调用 Retrofit API。
- 使用
StateFlow或LiveData来存储和暴露数据状态(成功、加载中、错误)。
- 在
- 更新 UI:
- 在
Activity或Fragment中,观察 ViewModel 暴露的LiveData或StateFlow。 - 在回调中更新 UI 组件(如
RecyclerView、ProgressBar、显示错误信息)。
- 在
遵循这个模式,你的网络请求代码将变得健壮、可测试且易于维护。
