引言
网络编程是现代 Android 应用开发中不可或缺的重要组成部分。随着移动互联网的快速发展,几乎所有的应用都需要与服务器进行数据交互,获取实时信息、同步用户数据或提供在线服务。Android 提供了丰富的网络编程 API 和第三方库,使开发者能够高效地实现各种网络功能。本文将深入探讨 Android 网络编程的核心概念、最佳实践和高级技巧。
网络编程基础
HTTP 协议基础
HTTP(HyperText Transfer Protocol)是现代网络应用的基础协议。在 Android 开发中,我们主要使用 HTTP/HTTPS 协议进行网络通信。
// HTTP 请求方法
enum class HttpMethod {
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
}
// HTTP 状态码
object HttpStatusCode {
const val OK = 200
const val CREATED = 201
const val NO_CONTENT = 204
const val BAD_REQUEST = 400
const val UNAUTHORIZED = 401
const val FORBIDDEN = 403
const val NOT_FOUND = 404
const val INTERNAL_SERVER_ERROR = 500
const val SERVICE_UNAVAILABLE = 503
}
// 基础网络请求数据类
data class NetworkRequest(
val url: String,
val method: HttpMethod = HttpMethod.GET,
val headers: Map<String, String> = emptyMap(),
val body: String? = null,
val timeout: Long = 30000L
)
data class NetworkResponse(
val statusCode: Int,
val headers: Map<String, List<String>>,
val body: String?,
val isSuccessful: Boolean = statusCode in 200..299
)
原生 HttpURLConnection
class HttpURLConnectionClient {
suspend fun executeRequest(request: NetworkRequest): Result<NetworkResponse> {
return withContext(Dispatchers.IO) {
try {
val url = URL(request.url)
val connection = url.openConnection() as HttpURLConnection
// 配置连接
connection.apply {
requestMethod = request.method.name
connectTimeout = request.timeout.toInt()
readTimeout = request.timeout.toInt()
doInput = true
// 设置请求头
request.headers.forEach { (key, value) ->
setRequestProperty(key, value)
}
// 处理请求体
if (request.body != null && request.method in listOf(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH)) {
doOutput = true
setRequestProperty("Content-Type", "application/json; charset=UTF-8")
outputStream.use { output ->
output.write(request.body.toByteArray(Charsets.UTF_8))
output.flush()
}
}
}
// 执行请求并处理响应
val statusCode = connection.responseCode
val headers = connection.headerFields
val responseBody = if (statusCode in 200..299) {
connection.inputStream.use { input ->
input.bufferedReader().use { reader ->
reader.readText()
}
}
} else {
connection.errorStream?.use { error ->
error.bufferedReader().use { reader ->
reader.readText()
}
}
}
connection.disconnect()
Result.success(
NetworkResponse(
statusCode = statusCode,
headers = headers,
body = responseBody
)
)
} catch (e: Exception) {
Result.failure(e)
}
}
}
// GET 请求
suspend fun get(
url: String,
headers: Map<String, String> = emptyMap()
): Result<NetworkResponse> {
return executeRequest(
NetworkRequest(
url = url,
method = HttpMethod.GET,
headers = headers
)
)
}
// POST 请求
suspend fun post(
url: String,
body: String,
headers: Map<String, String> = emptyMap()
): Result<NetworkResponse> {
return executeRequest(
NetworkRequest(
url = url,
method = HttpMethod.POST,
headers = headers,
body = body
)
)
}
// 文件下载
suspend fun downloadFile(
url: String,
outputFile: File,
progressCallback: ((bytesRead: Long, totalBytes: Long) -> Unit)? = null
): Result<File> {
return withContext(Dispatchers.IO) {
try {
val connection = URL(url).openConnection() as HttpURLConnection
connection.connectTimeout = 30000
connection.readTimeout = 30000
val totalBytes = connection.contentLengthLong
var bytesRead = 0L
connection.inputStream.use { input ->
outputFile.outputStream().use { output ->
val buffer = ByteArray(8192)
var bytes: Int
while (input.read(buffer).also { bytes = it } != -1) {
output.write(buffer, 0, bytes)
bytesRead += bytes
progressCallback?.invoke(bytesRead, totalBytes)
}
}
}
connection.disconnect()
Result.success(outputFile)
} catch (e: Exception) {
Result.failure(e)
}
}
}
}
OkHttp 网络库
OkHttp 基础使用
class OkHttpNetworkClient {
private val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(LoggingInterceptor())
.addInterceptor(AuthInterceptor())
.addNetworkInterceptor(CacheInterceptor())
.cache(createCache())
.build()
private fun createCache(): Cache {
val cacheDir = File(context.cacheDir, "http_cache")
val cacheSize = 10 * 1024 * 1024L // 10MB
return Cache(cacheDir, cacheSize)
}
// 同步请求
fun executeSync(request: Request): Response {
return client.newCall(request).execute()
}
// 异步请求
fun executeAsync(request: Request, callback: Callback) {
client.newCall(request).enqueue(callback)
}
// 协程支持
suspend fun execute(request: Request): Result<Response> {
return withContext(Dispatchers.IO) {
try {
val response = client.newCall(request).execute()
Result.success(response)
} catch (e: Exception) {
Result.failure(e)
}
}
}
// GET 请求
suspend fun get(
url: String,
headers: Map<String, String> = emptyMap()
): Result<String> {
val requestBuilder = Request.Builder().url(url)
headers.forEach { (key, value) ->
requestBuilder.addHeader(key, value)
}
return execute(requestBuilder.build()).mapCatching { response ->
response.body?.string() ?: ""
}
}
// POST JSON 请求
suspend fun postJson(
url: String,
json: String,
headers: Map<String, String> = emptyMap()
): Result<String> {
val mediaType = "application/json; charset=utf-8".toMediaType()
val requestBody = json.toRequestBody(mediaType)
val requestBuilder = Request.Builder()
.url(url)
.post(requestBody)
headers.forEach { (key, value) ->
requestBuilder.addHeader(key, value)
}
return execute(requestBuilder.build()).mapCatching { response ->
response.body?.string() ?: ""
}
}
// 文件上传
suspend fun uploadFile(
url: String,
file: File,
fieldName: String = "file",
additionalFields: Map<String, String> = emptyMap(),
progressCallback: ((bytesWritten: Long, totalBytes: Long) -> Unit)? = null
): Result<String> {
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.apply {
// 添加文件
val fileBody = file.asRequestBody("application/octet-stream".toMediaType())
val progressRequestBody = ProgressRequestBody(fileBody, progressCallback)
addFormDataPart(fieldName, file.name, progressRequestBody)
// 添加其他字段
additionalFields.forEach { (key, value) ->
addFormDataPart(key, value)
}
}
.build()
val request = Request.Builder()
.url(url)
.post(requestBody)
.build()
return execute(request).mapCatching { response ->
response.body?.string() ?: ""
}
}
// 文件下载
suspend fun downloadFile(
url: String,
outputFile: File,
progressCallback: ((bytesRead: Long, totalBytes: Long) -> Unit)? = null
): Result<File> {
return withContext(Dispatchers.IO) {
try {
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
throw IOException("Download failed: ${response.code}")
}
val body = response.body ?: throw IOException("Response body is null")
val totalBytes = body.contentLength()
var bytesRead = 0L
body.byteStream().use { input ->
outputFile.outputStream().use { output ->
val buffer = ByteArray(8192)
var bytes: Int
while (input.read(buffer).also { bytes = it } != -1) {
output.write(buffer, 0, bytes)
bytesRead += bytes
progressCallback?.invoke(bytesRead, totalBytes)
}
}
}
Result.success(outputFile)
} catch (e: Exception) {
Result.failure(e)
}
}
}
}
// 进度监听的 RequestBody
class ProgressRequestBody(
private val delegate: RequestBody,
private val progressCallback: ((bytesWritten: Long, totalBytes: Long) -> Unit)?
) : RequestBody() {
override fun contentType(): MediaType? = delegate.contentType()
override fun contentLength(): Long = delegate.contentLength()
override fun writeTo(sink: BufferedSink) {
val totalBytes = contentLength()
val progressSink = ProgressSink(sink, totalBytes, progressCallback)
val bufferedSink = progressSink.buffer()
delegate.writeTo(bufferedSink)
bufferedSink.flush()
}
}
class ProgressSink(
private val delegate: Sink,
private val totalBytes: Long,
private val progressCallback: ((bytesWritten: Long, totalBytes: Long) -> Unit)?
) : ForwardingSink(delegate) {
private var bytesWritten = 0L
override fun write(source: Buffer, byteCount: Long) {
super.write(source, byteCount)
bytesWritten += byteCount
progressCallback?.invoke(bytesWritten, totalBytes)
}
}
拦截器实现
// 日志拦截器
class LoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val startTime = System.nanoTime()
Log.d("HTTP", "Sending request ${request.url} on ${chain.connection()}")
Log.d("HTTP", "Headers: ${request.headers}")
val response = chain.proceed(request)
val endTime = System.nanoTime()
Log.d("HTTP", "Received response for ${response.request.url} in ${(endTime - startTime) / 1e6}ms")
Log.d("HTTP", "Response code: ${response.code}")
Log.d("HTTP", "Response headers: ${response.headers}")
return response
}
}
// 认证拦截器
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
// 添加认证头
val token = getAuthToken()
val authenticatedRequest = if (token != null) {
originalRequest.newBuilder()
.header("Authorization", "Bearer $token")
.build()
} else {
originalRequest
}
val response = chain.proceed(authenticatedRequest)
// 处理 401 未授权响应
if (response.code == 401) {
response.close()
// 尝试刷新 token
val newToken = refreshAuthToken()
if (newToken != null) {
val newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
return chain.proceed(newRequest)
}
}
return response
}
private fun getAuthToken(): String? {
// 从 SharedPreferences 或其他存储中获取 token
return null
}
private fun refreshAuthToken(): String? {
// 刷新 token 的逻辑
return null
}
}
// 缓存拦截器
class CacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 检查网络状态
val isNetworkAvailable = isNetworkAvailable()
val modifiedRequest = if (!isNetworkAvailable) {
// 无网络时强制使用缓存
request.newBuilder()
.cacheControl(
CacheControl.Builder()
.onlyIfCached()
.maxStale(7, TimeUnit.DAYS)
.build()
)
.build()
} else {
request
}
val response = chain.proceed(modifiedRequest)
return if (isNetworkAvailable) {
// 有网络时设置缓存策略
response.newBuilder()
.header("Cache-Control", "public, max-age=300") // 5分钟缓存
.removeHeader("Pragma")
.build()
} else {
response
}
}
private fun isNetworkAvailable(): Boolean {
// 检查网络连接状态
return true
}
}
// 重试拦截器
class RetryInterceptor(private val maxRetries: Int = 3) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
var response: Response? = null
var exception: IOException? = null
for (i in 0..maxRetries) {
try {
response?.close()
response = chain.proceed(request)
if (response.isSuccessful || !shouldRetry(response.code)) {
return response
}
} catch (e: IOException) {
exception = e
if (i == maxRetries) {
throw e
}
}
// 指数退避
if (i < maxRetries) {
val delay = (1000 * Math.pow(2.0, i.toDouble())).toLong()
Thread.sleep(delay)
}
}
return response ?: throw (exception ?: IOException("Unknown error"))
}
private fun shouldRetry(code: Int): Boolean {
return code in listOf(408, 429, 500, 502, 503, 504)
}
}
Retrofit 网络框架
Retrofit 基础配置
// API 接口定义
interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") userId: String): Response<User>
@GET("users")
suspend fun getUsers(
@Query("page") page: Int,
@Query("size") size: Int,
@Query("sort") sort: String? = null
): Response<PagedResponse<User>>
@POST("users")
suspend fun createUser(@Body user: CreateUserRequest): Response<User>
@PUT("users/{id}")
suspend fun updateUser(
@Path("id") userId: String,
@Body user: UpdateUserRequest
): Response<User>
@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") userId: String): Response<Unit>
@Multipart
@POST("upload")
suspend fun uploadFile(
@Part file: MultipartBody.Part,
@Part("description") description: RequestBody
): Response<UploadResponse>
@Streaming
@GET("download/{fileId}")
suspend fun downloadFile(@Path("fileId") fileId: String): Response<ResponseBody>
@FormUrlEncoded
@POST("auth/login")
suspend fun login(
@Field("username") username: String,
@Field("password") password: String
): Response<AuthResponse>
@Headers("Cache-Control: max-age=3600")
@GET("config")
suspend fun getConfig(): Response<AppConfig>
}
// 数据模型
data class User(
val id: String,
val username: String,
val email: String,
val fullName: String,
val avatarUrl: String?,
val createdAt: String,
val updatedAt: String
)
data class CreateUserRequest(
val username: String,
val email: String,
val password: String,
val fullName: String
)
data class UpdateUserRequest(
val email: String?,
val fullName: String?
)
data class PagedResponse<T>(
val data: List<T>,
val page: Int,
val size: Int,
val totalElements: Long,
val totalPages: Int
)
data class AuthResponse(
val accessToken: String,
val refreshToken: String,
val expiresIn: Long,
val user: User
)
data class UploadResponse(
val fileId: String,
val fileName: String,
val fileSize: Long,
val downloadUrl: String
)
data class AppConfig(
val apiVersion: String,
val features: Map<String, Boolean>,
val settings: Map<String, Any>
)
Retrofit 网络层实现
class NetworkModule {
companion object {
private const val BASE_URL = "https://api.example.com/v1/"
private const val CONNECT_TIMEOUT = 30L
private const val READ_TIMEOUT = 30L
private const val WRITE_TIMEOUT = 30L
}
@Provides
@Singleton
fun provideOkHttpClient(
loggingInterceptor: HttpLoggingInterceptor,
authInterceptor: AuthInterceptor,
cacheInterceptor: CacheInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.addInterceptor(authInterceptor)
.addInterceptor(cacheInterceptor)
.addInterceptor(loggingInterceptor)
.cache(createCache())
.build()
}
@Provides
@Singleton
fun provideRetrofit(
okHttpClient: OkHttpClient,
gsonConverterFactory: GsonConverterFactory
): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
@Provides
@Singleton
fun provideGsonConverterFactory(): GsonConverterFactory {
val gson = GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
return GsonConverterFactory.create(gson)
}
@Provides
@Singleton
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
}
private fun createCache(): Cache {
val cacheDir = File(context.cacheDir, "http_cache")
val cacheSize = 10 * 1024 * 1024L // 10MB
return Cache(cacheDir, cacheSize)
}
}
// Repository 层实现
class UserRepository(
private val apiService: ApiService,
private val userDao: UserDao
) {
suspend fun getUser(userId: String, forceRefresh: Boolean = false): Result<User> {
return try {
if (!forceRefresh) {
// 先尝试从本地数据库获取
val localUser = userDao.getUserById(userId)
if (localUser != null && !isDataStale(localUser.updatedAt)) {
return Result.success(localUser)
}
}
// 从网络获取
val response = apiService.getUser(userId)
if (response.isSuccessful) {
val user = response.body()!!
userDao.insertUser(user)
Result.success(user)
} else {
Result.failure(HttpException(response))
}
} catch (e: Exception) {
// 网络错误时尝试从本地获取
val localUser = userDao.getUserById(userId)
if (localUser != null) {
Result.success(localUser)
} else {
Result.failure(e)
}
}
}
suspend fun getUsers(
page: Int,
size: Int,
sort: String? = null
): Result<PagedResponse<User>> {
return try {
val response = apiService.getUsers(page, size, sort)
if (response.isSuccessful) {
val pagedResponse = response.body()!!
// 缓存用户数据
userDao.insertUsers(pagedResponse.data)
Result.success(pagedResponse)
} else {
Result.failure(HttpException(response))
}
} catch (e: Exception) {
Result.failure(e)
}
}
suspend fun createUser(request: CreateUserRequest): Result<User> {
return try {
val response = apiService.createUser(request)
if (response.isSuccessful) {
val user = response.body()!!
userDao.insertUser(user)
Result.success(user)
} else {
Result.failure(HttpException(response))
}
} catch (e: Exception) {
Result.failure(e)
}
}
suspend fun updateUser(userId: String, request: UpdateUserRequest): Result<User> {
return try {
val response = apiService.updateUser(userId, request)
if (response.isSuccessful) {
val user = response.body()!!
userDao.updateUser(user)
Result.success(user)
} else {
Result.failure(HttpException(response))
}
} catch (e: Exception) {
Result.failure(e)
}
}
suspend fun deleteUser(userId: String): Result<Unit> {
return try {
val response = apiService.deleteUser(userId)
if (response.isSuccessful) {
userDao.deleteUser(userId)
Result.success(Unit)
} else {
Result.failure(HttpException(response))
}
} catch (e: Exception) {
Result.failure(e)
}
}
suspend fun uploadAvatar(userId: String, imageFile: File): Result<String> {
return try {
val requestFile = imageFile.asRequestBody("image/*".toMediaType())
val body = MultipartBody.Part.createFormData("avatar", imageFile.name, requestFile)
val description = "User avatar".toRequestBody("text/plain".toMediaType())
val response = apiService.uploadFile(body, description)
if (response.isSuccessful) {
val uploadResponse = response.body()!!
Result.success(uploadResponse.downloadUrl)
} else {
Result.failure(HttpException(response))
}
} catch (e: Exception) {
Result.failure(e)
}
}
private fun isDataStale(updatedAt: String): Boolean {
// 检查数据是否过期(例如:超过1小时)
val updateTime = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
.parse(updatedAt)?.time ?: 0
val currentTime = System.currentTimeMillis()
return (currentTime - updateTime) > TimeUnit.HOURS.toMillis(1)
}
}
class HttpException(private val response: Response<*>) : Exception() {
val code: Int = response.code()
override val message: String = "HTTP ${response.code()} ${response.message()}"
}
WebSocket 实时通信
WebSocket 客户端实现
class WebSocketClient(
private val url: String,
private val okHttpClient: OkHttpClient
) {
private var webSocket: WebSocket? = null
private var isConnected = false
private var reconnectAttempts = 0
private val maxReconnectAttempts = 5
private val reconnectDelay = 5000L
private val listeners = mutableSetOf<WebSocketListener>()
interface WebSocketListener {
fun onConnected()
fun onDisconnected(code: Int, reason: String)
fun onMessage(message: String)
fun onError(throwable: Throwable)
}
fun connect() {
if (isConnected) return
val request = Request.Builder()
.url(url)
.build()
webSocket = okHttpClient.newWebSocket(request, object : okhttp3.WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
isConnected = true
reconnectAttempts = 0
notifyConnected()
}
override fun onMessage(webSocket: WebSocket, text: String) {
notifyMessage(text)
}
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
notifyMessage(bytes.utf8())
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
webSocket.close(1000, null)
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
isConnected = false
notifyDisconnected(code, reason)
// 自动重连
if (reconnectAttempts < maxReconnectAttempts) {
scheduleReconnect()
}
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
isConnected = false
notifyError(t)
// 自动重连
if (reconnectAttempts < maxReconnectAttempts) {
scheduleReconnect()
}
}
})
}
fun disconnect() {
webSocket?.close(1000, "Client disconnect")
webSocket = null
isConnected = false
}
fun sendMessage(message: String): Boolean {
return webSocket?.send(message) ?: false
}
fun sendMessage(bytes: ByteString): Boolean {
return webSocket?.send(bytes) ?: false
}
fun addListener(listener: WebSocketListener) {
listeners.add(listener)
}
fun removeListener(listener: WebSocketListener) {
listeners.remove(listener)
}
private fun scheduleReconnect() {
reconnectAttempts++
Handler(Looper.getMainLooper()).postDelayed({
if (!isConnected) {
connect()
}
}, reconnectDelay * reconnectAttempts)
}
private fun notifyConnected() {
listeners.forEach { it.onConnected() }
}
private fun notifyDisconnected(code: Int, reason: String) {
listeners.forEach { it.onDisconnected(code, reason) }
}
private fun notifyMessage(message: String) {
listeners.forEach { it.onMessage(message) }
}
private fun notifyError(throwable: Throwable) {
listeners.forEach { it.onError(throwable) }
}
}
// WebSocket 消息处理
class ChatWebSocketManager(
private val webSocketClient: WebSocketClient,
private val gson: Gson
) : WebSocketClient.WebSocketListener {
private val messageFlow = MutableSharedFlow<ChatMessage>()
private val connectionStateFlow = MutableStateFlow(ConnectionState.DISCONNECTED)
enum class ConnectionState {
CONNECTED, DISCONNECTED, CONNECTING, ERROR
}
data class ChatMessage(
val id: String,
val type: MessageType,
val content: String,
val senderId: String,
val timestamp: Long
)
enum class MessageType {
TEXT, IMAGE, FILE, SYSTEM
}
init {
webSocketClient.addListener(this)
}
fun connect() {
connectionStateFlow.value = ConnectionState.CONNECTING
webSocketClient.connect()
}
fun disconnect() {
webSocketClient.disconnect()
connectionStateFlow.value = ConnectionState.DISCONNECTED
}
fun sendMessage(content: String, type: MessageType = MessageType.TEXT) {
val message = ChatMessage(
id = UUID.randomUUID().toString(),
type = type,
content = content,
senderId = getCurrentUserId(),
timestamp = System.currentTimeMillis()
)
val json = gson.toJson(message)
webSocketClient.sendMessage(json)
}
fun observeMessages(): Flow<ChatMessage> = messageFlow.asSharedFlow()
fun observeConnectionState(): Flow<ConnectionState> = connectionStateFlow.asStateFlow()
override fun onConnected() {
connectionStateFlow.value = ConnectionState.CONNECTED
// 发送认证消息
sendAuthMessage()
}
override fun onDisconnected(code: Int, reason: String) {
connectionStateFlow.value = ConnectionState.DISCONNECTED
}
override fun onMessage(message: String) {
try {
val chatMessage = gson.fromJson(message, ChatMessage::class.java)
messageFlow.tryEmit(chatMessage)
} catch (e: Exception) {
Log.e("WebSocket", "Failed to parse message: $message", e)
}
}
override fun onError(throwable: Throwable) {
connectionStateFlow.value = ConnectionState.ERROR
Log.e("WebSocket", "WebSocket error", throwable)
}
private fun sendAuthMessage() {
val authToken = getAuthToken()
val authMessage = mapOf(
"type" to "auth",
"token" to authToken
)
val json = gson.toJson(authMessage)
webSocketClient.sendMessage(json)
}
private fun getCurrentUserId(): String {
// 获取当前用户ID
return "current_user_id"
}
private fun getAuthToken(): String {
// 获取认证token
return "auth_token"
}
}
网络安全
HTTPS 和证书固定
class SecurityConfig {
companion object {
fun createSecureOkHttpClient(context: Context): OkHttpClient {
return OkHttpClient.Builder()
.certificatePinner(createCertificatePinner())
.hostnameVerifier(createHostnameVerifier())
.sslSocketFactory(
createSSLSocketFactory(context),
createTrustManager(context)
)
.build()
}
private fun createCertificatePinner(): CertificatePinner {
return CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
.build()
}
private fun createHostnameVerifier(): HostnameVerifier {
return HostnameVerifier { hostname, session ->
// 自定义主机名验证逻辑
HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session)
}
}
private fun createSSLSocketFactory(context: Context): SSLSocketFactory {
val trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
)
val keyStore = KeyStore.getInstance("BKS")
context.assets.open("certificates.bks").use { inputStream ->
keyStore.load(inputStream, "password".toCharArray())
}
trustManagerFactory.init(keyStore)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustManagerFactory.trustManagers, null)
return sslContext.socketFactory
}
private fun createTrustManager(context: Context): X509TrustManager {
val trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
)
val keyStore = KeyStore.getInstance("BKS")
context.assets.open("certificates.bks").use { inputStream ->
keyStore.load(inputStream, "password".toCharArray())
}
trustManagerFactory.init(keyStore)
return trustManagerFactory.trustManagers[0] as X509TrustManager
}
}
}
// 请求签名
class RequestSigner {
private val secretKey = "your_secret_key"
fun signRequest(request: NetworkRequest): NetworkRequest {
val timestamp = System.currentTimeMillis().toString()
val nonce = UUID.randomUUID().toString()
val signatureData = buildString {
append(request.method.name)
append(request.url)
append(request.body ?: "")
append(timestamp)
append(nonce)
}
val signature = generateHMAC(signatureData, secretKey)
val headers = request.headers.toMutableMap().apply {
put("X-Timestamp", timestamp)
put("X-Nonce", nonce)
put("X-Signature", signature)
}
return request.copy(headers = headers)
}
private fun generateHMAC(data: String, key: String): String {
val mac = Mac.getInstance("HmacSHA256")
val secretKeySpec = SecretKeySpec(key.toByteArray(), "HmacSHA256")
mac.init(secretKeySpec)
val hashBytes = mac.doFinal(data.toByteArray())
return Base64.encodeToString(hashBytes, Base64.NO_WRAP)
}
}
性能优化
网络请求优化
class NetworkOptimizer {
// 请求合并
class RequestBatcher<T>(
private val batchSize: Int = 10,
private val batchTimeout: Long = 100L,
private val executor: suspend (List<T>) -> List<Any>
) {
private val pendingRequests = mutableListOf<BatchRequest<T>>()
private var batchJob: Job? = null
data class BatchRequest<T>(
val data: T,
val deferred: CompletableDeferred<Any>
)
suspend fun execute(data: T): Any {
val deferred = CompletableDeferred<Any>()
val request = BatchRequest(data, deferred)
synchronized(pendingRequests) {
pendingRequests.add(request)
if (pendingRequests.size >= batchSize) {
processBatch()
} else if (batchJob == null) {
batchJob = CoroutineScope(Dispatchers.IO).launch {
delay(batchTimeout)
synchronized(pendingRequests) {
if (pendingRequests.isNotEmpty()) {
processBatch()
}
}
}
}
}
return deferred.await()
}
private suspend fun processBatch() {
val batch = pendingRequests.toList()
pendingRequests.clear()
batchJob?.cancel()
batchJob = null
try {
val results = executor(batch.map { it.data })
batch.forEachIndexed { index, request ->
request.deferred.complete(results[index])
}
} catch (e: Exception) {
batch.forEach { request ->
request.deferred.completeExceptionally(e)
}
}
}
}
// 连接池优化
fun createOptimizedOkHttpClient(): OkHttpClient {
val connectionPool = ConnectionPool(
maxIdleConnections = 10,
keepAliveDuration = 5,
timeUnit = TimeUnit.MINUTES
)
return OkHttpClient.Builder()
.connectionPool(connectionPool)
.protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
.build()
}
// 请求去重
class RequestDeduplicator {
private val ongoingRequests = mutableMapOf<String, Deferred<Any>>()
suspend fun <T> execute(key: String, request: suspend () -> T): T {
val existingRequest = ongoingRequests[key]
return if (existingRequest != null) {
@Suppress("UNCHECKED_CAST")
existingRequest.await() as T
} else {
val deferred = CoroutineScope(Dispatchers.IO).async {
try {
request()
} finally {
ongoingRequests.remove(key)
}
}
ongoingRequests[key] = deferred
deferred.await()
}
}
}
}
总结
Android 网络编程是移动应用开发的核心技能之一。通过本文的深入探讨,我们学习了:
- 基础概念:HTTP 协议、原生 HttpURLConnection 的使用
- OkHttp 框架:强大的网络库及其拦截器机制
- Retrofit 框架:类型安全的 REST API 客户端
- WebSocket:实时双向通信的实现
- 网络安全:HTTPS、证书固定、请求签名等安全措施
- 性能优化:请求合并、连接池、去重等优化技巧
掌握这些技能后,开发者可以构建出高效、安全、可靠的网络层,为用户提供流畅的网络体验。在实际开发中,应该根据具体需求选择合适的网络方案,并始终关注性能、安全性和用户体验。
上一篇 下一篇