目录
- 核心概念:客户端-服务器模型
- Android 网络权限配置
- Android 网络请求的限制(主线程问题)
- 主流的连接方式与库
- HttpURLConnection (原生、轻量)
- OkHttp (现代、高效、推荐)
- Retrofit (类型安全、优雅、推荐)
- 数据交换格式:JSON
- 实战演练:使用 OkHttp 和 Retrofit 连接一个 RESTful API
- 高级主题
- HTTPS 与网络安全
- 处理身份验证
- 连接状态管理
- 总结与学习路径
核心概念:客户端-服务器模型
想象一下你去餐厅吃饭:

- 你的手机 (Android App):是 客户端,它主动发起请求,给我一份菜单”。
- 餐厅的厨房 (服务器):是 服务器,它接收请求,进行处理(准备菜单),然后将结果(菜单)返回给你。
在开发中,这个过程是通过 HTTP/HTTPS 协议实现的,客户端发送一个 HTTP 请求,服务器处理后返回一个 HTTP 响应。
常见的 HTTP 请求方法:
GET:获取数据(如获取用户列表)。POST:提交数据(如注册新用户)。PUT:更新数据(如修改用户信息)。DELETE:删除数据(如删除一条帖子)。
Android 网络权限配置
你的 App 需要明确告诉操作系统:“我需要访问网络!”,这通过在 AndroidManifest.xml 文件中添加 <uses-permission> 标签来实现。
<!-- 在 <manifest> 标签内添加 --> <uses-permission android:name="android.permission.INTERNET" />
注意:从 Android 9 (API 28) 开始,默认情况下,App 不能使用明文的 HTTP 流量,必须使用 HTTPS,如果你的服务器暂时不支持 HTTPS,你可以在 AndroidManifest.xml 的 <application> 标签中添加 android:usesCleartextTraffic="true" 来允许(但不推荐,仅用于开发测试)。

<!-- 在 <application> 标签内添加 -->
<application
...
android:usesCleartextTraffic="true">
...
</application>
Android 网络请求的限制(主线程问题)
这是 Android 开发中最重要的规则之一!
为了保持用户界面的流畅,Android 的 UI 操作(如更新按钮文字、显示列表)必须在 主线程(或 UI 线程)上执行。
网络请求是一个耗时操作,它需要等待服务器响应,这个过程可能从几百毫秒到几秒不等,如果在主线程上执行网络请求,你的 App 的 UI 会被“卡住”,系统会抛出一个 NetworkOnMainThreadException 异常,甚至可能导致系统弹出“应用无响应”(ANR) 的对话框。
解决方案:所有的网络请求都必须在 后台线程 中执行。

主流的连接方式与库
在 Android 中,你有以下几种方式来执行网络请求:
HttpURLConnection (原生、轻量)
这是 Android SDK 自带的 API,无需添加第三方库,它功能全面,但 API 相对繁琐,需要手动处理线程、输入输出流等。
适用场景:对于非常简单的 App,或者想不依赖第三方库的场景。
代码示例 (Kotlin):
// 必须在后台线程执行
Thread {
val url = URL("https://api.example.com/users")
val urlConnection = url.openConnection() as HttpURLConnection
try {
urlConnection.requestMethod = "GET"
urlConnection.connectTimeout = 5000 // 5秒连接超时
urlConnection.readTimeout = 5000 // 5秒读取超时
val responseCode = urlConnection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
val inputStream = urlConnection.inputStream
val response = inputStream.bufferedReader().use { it.readText() }
// 在这里处理 response,例如通过 Handler 更新 UI
Log.d("Network", "Response: $response")
} else {
Log.e("Network", "Error: HTTP $responseCode")
}
} finally {
urlConnection.disconnect()
}
}.start()
缺点:代码冗长,需要自己管理线程,处理 JSON 等也比较麻烦。
OkHttp (现代、高效、推荐)
OkHttp 是目前最流行的 HTTP 客户端之一,它由 Square 公司(也是 Retrofit 的开发者)维护,以其高效、简洁和强大的功能而闻名。
优点:
- 支持同步和异步请求。
- 内置连接池,复用 TCP 连接,性能优异。
- 支持 GZIP 压缩,减少数据传输量。
- 简单易用的拦截器机制,可以统一处理日志、缓存等。
添加依赖:
// build.gradle (Module :app)
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0") // 使用最新版本
}
代码示例 (Kotlin):
// 1. 创建 OkHttpClient 实例(通常作为单例)
val client = OkHttpClient()
// 2. 创建一个 Request
val request = Request.Builder()
.url("https://api.example.com/users")
.get()
.build()
// 3. 异步执行请求(推荐)
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 请求失败,例如网络错误、超时
e.printStackTrace()
// 在这里更新 UI,提示用户网络错误
}
override fun onResponse(call: Call, response: Response) {
// 请求成功
response.use { // use 确保response.body()被关闭
if (it.isSuccessful) {
val responseBody = it.body?.string()
// 在这里处理 responseBody,并通过 Handler 或 LiveData 更新 UI
Log.d("OkHttp", "Response: $responseBody")
} else {
// 服务器返回了错误状态码,如 404, 500
Log.e("OkHttp", "Error: ${it.code}")
}
}
}
})
OkHttp 已经帮你处理了线程问题,enqueue 方法会自动在后台线程执行请求,并通过回调将结果返回。
Retrofit (类型安全、优雅、推荐)
Retrofit 本身不执行网络请求,它是一个 类型安全的 HTTP 客户端,它的核心作用是将一个 HTTP API 的接口转换成一个可调用的 Java/Kotlin 对象,它内部默认使用 OkHttp 来执行网络请求。
优点:
- 接口定义:通过定义接口来描述 API,代码清晰、易于维护。
- 类型安全:自动将 JSON 响应体映射到 Kotlin/Java 对象,无需手动解析。
- 支持多种适配器:可以轻松切换为 RxJava、Kotlin Coroutines 等异步处理方式。
- 强大的转换器:支持 JSON、XML、Protobuf 等多种数据格式。
添加依赖:
// build.gradle (Module :app)
dependencies {
// 1. Retrofit 核心库
implementation("com.squareup.retrofit2:retrofit:2.9.0") // 使用最新版本
// 2. JSON 转换器 (Gson 是最常用的)
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// 3. 如果想使用 Coroutines (推荐)
implementation("com.squareup.retrofit2:converter-scalars:2.9.0") // 可选,用于处理原始字符串
implementation("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2") // 可选,旧版本
// 新版本 Coroutines Adapter 可能需要不同的配置,通常直接使用 suspend 函数即可
}
实战演练:
假设服务器 API 是 https://api.example.com/users,返回一个 JSON 数组。
定义数据模型:
// User.kt
data class User(
val id: Int,
val name: String,
val email: String
)
定义 API 接口:
// ApiService.kt
import retrofit2.http.GET
interface ApiService {
@GET("users")
suspend fun getUsers(): List<User> // 使用 suspend 函数,可以在协程中直接调用
}
创建 Retrofit 实例:
// RetrofitClient.kt (通常是一个单例对象)
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
private const val BASE_URL = "https://api.example.com/"
val instance: ApiService by lazy {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) // 添加 Gson 转换器
.build()
retrofit.create(ApiService::class.java)
}
}
在 ViewModel 或协程中调用:
// 在 ViewModel 或 Activity/Fragment 的协程作用域中调用
// 在 ViewModel 中
class MyViewModel : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> = _users
fun fetchUsers() {
viewModelScope.launch { // viewModelScope 会自动在配置更改时取消
try {
val userList = RetrofitClient.instance.getUsers()
_users.postValue(userList) // postValue 用于在后台线程更新 LiveData
} catch (e: Exception) {
// 处理网络错误或解析错误
Log.e("MyViewModel", "Failed to fetch users", e)
}
}
}
}
Retrofit + OkHttp + Coroutines 是目前 Android 官方推荐和社区最主流的组合,代码非常简洁和现代化。
数据交换格式:JSON
服务器和客户端之间通常使用 JSON (JavaScript Object Notation) 格式来交换数据,因为它轻量、易于人阅读,并且可以被大多数编程语言轻松解析。
- 服务器响应:
[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}] - 客户端模型:
List<User>(如上例所示)
Retrofit 的 Gson/Jackson 转换器会自动完成 JSON 字符串到 Kotlin 对象的转换。
实战演练:使用 OkHttp 和 Retrofit 连接一个 RESTful API
假设我们要连接一个公共的测试 API:JSONPlaceholder。
目标:获取所有 Todo 列表。
添加依赖 (OkHttp + Retrofit + Gson)。
定义数据模型:
// Todo.kt
data class Todo(
val userId: Int,
val id: Int,
val title: String,
val completed: Boolean
)
定义 API 接口:
// TodoApiService.kt
import retrofit2.http.GET
interface TodoApiService {
@GET("todos")
suspend fun getTodos(): List<Todo>
}
创建 Retrofit 客户端:
// RetrofitClient.kt
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
private const val BASE_URL = "https://jsonplaceholder.typicode.com/"
val instance: TodoApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TodoApiService::class.java)
}
}
在 ViewModel 中调用:
// TodoViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class TodoViewModel : ViewModel() {
private val _todos = MutableLiveData<List<Todo>>()
val todos: LiveData<List<Todo>> = _todos
fun loadTodos() {
viewModelScope.launch {
try {
val fetchedTodos = RetrofitClient.instance.getTodos()
_todos.postValue(fetchedTodos)
} catch (e: Exception) {
// 处理错误
_todos.postValue(emptyList()) // 可以设置为空列表或显示错误状态
e.printStackTrace()
}
}
}
}
在 UI 中观察数据并显示 (例如使用 Jetpack Compose):
// TodoScreen.kt
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun TodoScreen(todoViewModel: TodoViewModel = viewModel()) {
// 当进入 Composable 时,触发数据加载
LaunchedEffect(key1 = true) {
todoViewModel.loadTodos()
}
val todos by todoViewModel.todos.collectAsState()
if (todos.isEmpty()) {
// 可以显示一个加载指示器
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator()
}
} else {
Column(modifier = Modifier.padding(16.dp)) {
Text("Todo List", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(8.dp))
todos.forEach { todo ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
) {
Text(
text = "${todo.id}: ${todo.title}",
modifier = Modifier.padding(16.dp)
)
}
}
}
}
}
高级主题
HTTPS 与网络安全
- 为什么必须用 HTTPS?
- 加密:防止数据在传输过程中被窃听。
- 身份验证:确保你连接的是真正的服务器,而不是中间人攻击。
- 如何配置?
- 服务器端:你的服务器必须配置 SSL 证书(可以是 Let's Encrypt 的免费证书)。
- 客户端 (Android):现代 Android 版本默认只信任受信任的 CA (证书颁发机构) 签发的证书,如果你的服务器使用自签名证书,你需要在 App 中进行额外的信任配置,这通常不推荐,因为会降低安全性。
处理身份验证
很多 API 需要身份验证才能访问,常见方式:
- API Key:通常放在请求头中。
// 在 Retrofit 接口中 @GET("users") suspend fun getUsers( @Header("Authorization") apiKey: String // 或者 @HeaderMap ): List<User> - Bearer Token (JWT):最流行的现代方式,也放在请求头中。
@GET("profile") suspend fun getProfile( @Header("Authorization") token: String // "Bearer your_token_here" ): UserProfile - OAuth 2.0:更复杂的流程,通常用于第三方登录(如 Google, Facebook)。
连接状态管理
一个好的 App 应该能感知网络状态。
- 显示加载指示器:当请求正在进行时,显示一个进度条或旋转动画。
- 处理无网络情况:检测到网络不可用时,禁用相关操作,并提示用户。
- 缓存策略:使用 OkHttp 的缓存机制,让用户在没有网络时也能查看上次加载的数据。
总结与学习路径
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HttpURLConnection | 原生,无需依赖 | API繁琐,需手动管理线程 | 简单工具类、学习原理 |
| OkHttp | 高效、简洁、功能强大 | 需要手动解析JSON | 大多数网络请求,作为Retrofit的基础 |
| Retrofit | 类型安全、接口化、易于维护 | 需要额外学习成本 | 强烈推荐,所有需要与服务器交互的现代App |
推荐学习路径:
- 理解基础:先搞懂 HTTP 协议、GET/POST 请求、JSON 格式和 Android 的主线程限制。
- 从 OkHttp 开始:尝试使用 OkHttp 发送一个简单的 GET 请求,感受一下异步回调的机制。
- 拥抱 Retrofit:学习如何定义 API 接口,并结合数据模型和 JSON 转换器,这是你未来开发的主力工具。
- 结合现代架构:将 Retrofit 与 ViewModel、LiveData/StateFlow 和 Kotlin Coroutines 结合起来,构建一个响应式、健壮的网络层。
- 深入高级特性:学习 OkHttp 的拦截器、Retrofit 的适配器(如 RxJava)以及如何处理复杂的认证和错误。
希望这份详细的指南能帮助你顺利地在 Android 开发中连接服务器!祝你编码愉快!
