客户端-服务器模型
要理解基本的工作流程:

- Android 客户端:你的 App,负责收集用户数据,并通过网络请求发送给服务器。
- 服务器:一个后端服务(通常用 Java, Python, Node.js, PHP 等语言编写),负责接收、处理、存储数据,并可能返回响应。
- 网络协议:最常用的是 HTTP/HTTPS,HTTPS 是加密的 HTTP,是现在 App 开发的强制标准,用于保护数据安全。
使用 HttpURLConnection (原生 API)
这是 Android SDK 自带的传统方式,无需引入第三方库,虽然代码稍显繁琐,但它是理解网络请求的基础。
特点:
- 优点:无需额外依赖,是 Android 平台的一部分。
- 缺点:API 较为底层,代码冗长,处理异步(在子线程中执行)和回调比较麻烦。
示例:发送 POST 请求(JSON 数据)
假设我们要向 https://api.example.com/users 发送一个 JSON 对象 {"name": "John", "email": "john@example.com"}。
添加网络权限
在 app/src/main/AndroidManifest.xml 中添加:

<uses-permission android:name="android.permission.INTERNET" />
在后台线程中执行网络请求
网络操作不能在主线程(UI线程)中执行,否则会抛出 NetworkOnMainThreadException 异常,我们可以使用 Thread + Handler,或者更现代的 Kotlin Coroutines/RxJava,这里展示使用 Thread + Handler 的方式。
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.io.BufferedReader
import java.io.DataOutputStream
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class MainActivity : AppCompatActivity() {
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val sendButton: Button = findViewById(R.id.send_button)
sendButton.setOnClickListener {
sendDataToServer()
}
}
private fun sendDataToServer() {
// 在子线程中执行网络请求
Thread {
val apiUrl = "https://api.example.com/users"
val jsonBody = """{"name": "John", "email": "john@example.com"}"""
try {
val url = URL(apiUrl)
val urlConnection = url.openConnection() as HttpURLConnection
urlConnection.requestMethod = "POST"
urlConnection.setRequestProperty("Content-Type", "application/json; utf-8")
urlConnection.setRequestProperty("Accept", "application/json")
urlConnection.doOutput = true // 允许发送请求体
// 发送请求体
DataOutputStream(urlConnection.outputStream).use { os ->
os.writeBytes(jsonBody)
}
// 获取响应码
val responseCode = urlConnection.responseCode
Log.d("Network", "Response Code: $responseCode")
// 读取响应
val response = if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED) {
urlConnection.inputStream.bufferedReader().use { it.readText() }
} else {
urlConnection.errorStream.bufferedReader().use { it.readText() }
}
Log.d("Network", "Response: $response")
// 通过 Handler 切换回主线程更新 UI
handler.post {
Toast.makeText(this@MainActivity, "发送成功! 响应: $response", Toast.LENGTH_LONG).show()
}
} catch (e: Exception) {
e.printStackTrace()
handler.post {
Toast.makeText(this@MainActivity, "发送失败: ${e.message}", Toast.LENGTH_LONG).show()
}
}
}.start()
}
}
使用第三方库 (强烈推荐)
手动管理 HttpURLConnection 非常繁琐,业界广泛使用第三方库来简化网络请求。
Retrofit + OkHttp (当前业界标准)
这是目前最流行、最强大的组合,Retrofit 负责将 API 接口定义和调用转换成底层的 OkHttp 请求,OkHttp 则是一个高效的 HTTP 客户端。

特点:
- 类型安全:通过接口定义 API,编译时检查。
- 简洁易用:注解驱动的 API 声明,代码非常清晰。
- 高性能:底层基于 OkHttp,支持异步、同步请求。
- 可扩展:与 Gson/Moshi (JSON 解析)、RxJava/Kotlin Coroutines (异步处理) 无缝集成。
步骤:
添加依赖
在 app/build.gradle.kts (或 build.gradle) 中:
// OkHttp (Retrofit 依赖)
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// JSON 转换器 (这里用 Gson)
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// Coroutines 支持 (推荐)
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
添加网络权限
同上,在 AndroidManifest.xml 中添加 <uses-permission android:name="android.permission.INTERNET" />。
创建数据类
// 请求体
data class UserRequest(
val name: String,
val email: String
)
// 响应体 (根据服务器返回结构定义)
data class UserResponse(
val id: String,
val name: String,
val email: String,
val createdAt: String
)
定义 Retrofit API 接口
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
interface ApiService {
@POST("users") // 对应服务器 API 的路径
fun createUser(@Body userRequest: UserRequest): Call<UserResponse>
}
创建 Retrofit 实例并进行网络调用
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class RetrofitClient {
companion object {
private const val BASE_URL = "https://api.example.com/"
val instance: ApiService by lazy {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(ApiService::class.java)
}
}
}
在 Activity/ViewModel 中调用
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class RetrofitActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_retrofit)
val sendButton: Button = findViewById(R.id.send_button_retrofit)
sendButton.setOnClickListener {
sendPostRequestWithRetrofit()
}
}
private fun sendPostRequestWithRetrofit() {
val apiService = RetrofitClient.instance
val userRequest = UserRequest(name = "Jane Doe", email = "jane@example.com")
// 使用协程进行异步调用 (推荐)
lifecycleScope.launch {
try {
val response = apiService.createUser(userRequest)
if (response.isSuccessful) {
val userResponse = response.body()
Log.d("Retrofit", "Success: ${userResponse.toString()}")
Toast.makeText(this@RetrofitActivity, "发送成功: ${userResponse?.name}", Toast.LENGTH_SHORT).show()
} else {
Log.e("Retrofit", "Error: ${response.code()} - ${response.message()}")
Toast.makeText(this@RetrofitActivity, "发送失败: ${response.message()}", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
Log.e("Retrofit", "Exception: ${e.message}", e)
Toast.makeText(this@RetrofitActivity, "发送失败: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
// 或者使用传统的 Callback 方式
/*
apiService.createUser(userRequest).enqueue(object : Callback<UserResponse> {
override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
if (response.isSuccessful) {
val userResponse = response.body()
Log.d("Retrofit", "Success: ${userResponse.toString()}")
Toast.makeText(this@RetrofitActivity, "发送成功: ${userResponse?.name}", Toast.LENGTH_SHORT).show()
} else {
Log.e("Retrofit", "Error: ${response.code()} - ${response.message()}")
Toast.makeText(this@RetrofitActivity, "发送失败: ${response.message()}", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<UserResponse>, t: Throwable) {
Log.e("Retrofit", "Failure: ${t.message}", t)
Toast.makeText(this@RetrofitActivity, "发送失败: ${t.message}", Toast.LENGTH_SHORT).show()
}
})
*/
}
}
使用 Volley (Google 官方库)
Volley 是 Google 推出的一个网络通信库,特别适合数据量不大、但通信频繁的场景。
特点:
- 优点:自动请求调度,缓存支持,图片加载方便,API 简单。
- 缺点:对于大文件上传/下载支持不佳,不适合流式数据。
由于 Retrofit 的普及,Volley 现在用得相对少了,但了解它依然有价值。
最佳实践和重要考虑因素
-
永远不要在主线程进行网络请求
- 这会导致应用卡顿,甚至被 Android 系统强制关闭 (ANR - Application Not Responding)。
- 解决方案:使用
Kotlin Coroutines、RxJava或ExecutorService。
-
使用 HTTPS
- 所有网络通信都必须使用 HTTPS 协议,以加密数据,防止中间人攻击和窃听。
- 在
AndroidManifest.xml中强制使用网络安全配置。
-
处理错误和异常
- 网络是不可靠的,必须处理各种异常情况:
SocketTimeoutException:连接超时。UnknownHostException:无法解析域名。SSLHandshakeException:证书验证失败。
- 同时要处理服务器返回的错误状态码(如 404 Not Found, 500 Internal Server Error)。
- 网络是不可靠的,必须处理各种异常情况:
-
数据格式
- JSON (JavaScript Object Notation):目前最主流的格式,轻量级、易于人阅读和机器解析,Retrofit + Gson/Moshi 是完美组合。
- Form-Data:用于传统的表单提交,
application/x-www-form-urlencoded。 - Multipart:用于上传文件,
multipart/form-data。
-
安全性
- 敏感数据:密码、Token 等敏感信息不要通过 GET 请求(URL 参数)发送,必须放在 POST 请求的请求体中。
- 存储 Token:从服务器获取的认证 Token (如 JWT) 应该安全存储,优先使用
EncryptedSharedPreferences进行加密存储,而不是普通的SharedPreferences。
-
日志
- 在开发阶段,使用
HttpLoggingInterceptor(OkHttp的一部分) 来打印详细的网络请求和响应日志,非常有助于调试。 - 在生产环境中,关闭或控制日志级别,避免泄露敏感信息。
- 在开发阶段,使用
总结与选择
| 特性 | HttpURLConnection |
Volley | Retrofit + OkHttp |
|---|---|---|---|
| 易用性 | 差,代码冗长 | 中等 | 优秀,注解驱动 |
| 性能 | 一般 | 良好 | 优秀,异步高效 |
| 功能 | 基础 | 内置缓存、图片 | 高度可扩展,支持多种格式和异步方案 |
| 依赖 | 无需 | 需要引入 | 需要引入 |
| 推荐场景 | 简单 demo,不想引入第三方库 | 小型应用,数据请求频繁但不大 | 几乎所有现代 Android 应用的首选 |
对于任何新的 Android 项目,强烈推荐使用 Retrofit + OkHttp 的组合,它代表了当前业界最佳实践,能让你写出更健壮、更易维护、更高效的代码,如果你正在维护一个老项目,并且项目已经使用了 Volley,那么可以继续使用,但在新功能开发时,考虑迁移到 Retrofit。HttpURLConnection 只适合用于学习或极特殊的环境。
