SpringBoot 2.x OkHttp 4.9.3 实战Android 登录功能 3 种网络异常排查与修复在前后端分离的移动应用开发中网络通信是最基础也最容易出问题的环节。当Android客户端与SpringBoot后端进行交互时开发者经常会遇到各种网络异常特别是在Android 9及以上版本中网络策略的变化带来了新的挑战。本文将深入分析三种最常见的网络异常场景并提供完整的解决方案。1. Android 9 CLEARTEXT 通信异常与安全配置Android 9API级别28引入了一项重要的安全变更默认禁止非加密的HTTP明文通信。这个改动导致很多原本在低版本Android上运行正常的应用突然出现网络请求失败。1.1 异常现象与根因分析当应用尝试向HTTP端点发起请求时会在Logcat中看到如下错误java.net.UnknownServiceException: CLEARTEXT communication to 192.168.1.100 not permitted by network security policy这是因为Android 9默认要求所有网络通信必须使用TLS加密HTTPS。如果后端服务暂时没有配置HTTPS我们需要在应用中明确允许明文通信。1.2 解决方案network_security_config.xml配置在res/xml目录下创建network_security_config.xml文件?xml version1.0 encodingutf-8? network-security-config domain-config cleartextTrafficPermittedtrue domain includeSubdomainstrue192.168.1.100/domain domain includeSubdomainstruelocalhost/domain /domain-config /network-security-config然后在AndroidManifest.xml中引用这个配置application android:networkSecurityConfigxml/network_security_config ... /application提示生产环境强烈建议使用HTTPS而非明文通信。上述方案仅适用于开发测试环境或内部网络。1.3 进阶安全配置建议如果必须允许明文通信建议至少限制允许的域名domain-config cleartextTrafficPermittedtrue domain includeSubdomainstrueyour.dev.server/domain /domain-config2. UnknownServiceException证书与SSL握手问题当后端使用自签名证书或证书链不完整时OkHttp会抛出UnknownServiceException。2.1 异常场景分析典型的错误日志如下javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.2.2 解决方案自定义信任管理器对于开发环境可以创建不验证证书的OkHttpClientfun createUnsafeOkHttpClient(): OkHttpClient { val trustAllCerts arrayOfTrustManager(object : X509TrustManager { override fun checkClientTrusted(chain: Arrayout X509Certificate?, authType: String?) {} override fun checkServerTrusted(chain: Arrayout X509Certificate?, authType: String?) {} override fun getAcceptedIssuers() arrayOfX509Certificate() }) val sslContext SSLContext.getInstance(SSL) sslContext.init(null, trustAllCerts, SecureRandom()) val sslSocketFactory sslContext.socketFactory return OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager) .hostnameVerifier { _, _ - true } .build() }警告此方案会完全禁用SSL验证仅适用于测试环境。生产环境应使用正规CA签发的证书。2.3 生产环境证书固定方案对于生产环境建议实施证书固定(Certificate Pinning)val certificatePinner CertificatePinner.Builder() .add(api.yourdomain.com, sha256/YourPublicKeyHashHere) .build() val okHttpClient OkHttpClient.Builder() .certificatePinner(certificatePinner) .build()3. 连接超时与局域网通信问题在开发测试阶段Android设备与本地开发服务器之间的通信经常遇到连接超时问题。3.1 常见错误场景java.net.SocketTimeoutException: failed to connect to 192.168.1.100/192.168.1.100:8080 (port 8080) from /192.168.1.101 (port 45678) after 10000ms3.2 排查步骤与解决方案网络连通性检查确保手机和开发电脑在同一局域网在手机浏览器中访问http://电脑IP:8080/health测试连通性OkHttp超时配置优化val okHttpClient OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build()SpringBoot后端配置调整在application.properties中添加server.address0.0.0.0这样SpringBoot会监听所有网络接口而不仅仅是localhost。3.3 高级调试技巧使用OkHttp的日志拦截器可以帮助诊断网络问题val loggingInterceptor HttpLoggingInterceptor().apply { level HttpLoggingInterceptor.Level.BODY } val client OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build()记得在发布版本中移除或禁用此拦截器。4. 综合实战健壮的登录功能实现结合上述解决方案我们来实现一个完整的登录功能包含错误处理和重试机制。4.1 网络请求封装class AuthRepository(private val okHttpClient: OkHttpClient) { suspend fun login(username: String, password: String): ResultAuthResponse { return try { val requestBody FormBody.Builder() .add(username, username) .add(password, password) .build() val request Request.Builder() .url(${BASE_URL}/login) .post(requestBody) .build() val response okHttpClient.newCall(request).await() if (response.isSuccessful) { val authResponse parseResponse(response) Result.success(authResponse) } else { Result.failure(Exception(Login failed: ${response.code})) } } catch (e: Exception) { Result.failure(e) } } private fun parseResponse(response: Response): AuthResponse { // 解析响应体 } }4.2 网络状态检测在发起请求前检查网络状态fun isNetworkAvailable(context: Context): Boolean { val connectivityManager context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager return connectivityManager.activeNetwork?.let { network - connectivityManager.getNetworkCapabilities(network)?.run { hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) } } ?: false }4.3 完整的登录流程处理class LoginViewModel : ViewModel() { private val authRepository AuthRepository(createOkHttpClient()) fun login(username: String, password: String) { viewModelScope.launch { _loginState.value LoginState.Loading try { when (val result authRepository.login(username, password)) { is Result.Success - { _loginState.value LoginState.Success(result.data) } is Result.Failure - { _loginState.value LoginState.Error(result.exception.message) } } } catch (e: Exception) { _loginState.value LoginState.Error(e.localizedMessage) } } } }在实际项目中遇到网络问题时建议按照以下优先级进行排查检查AndroidManifest.xml中的网络权限验证网络连通性能否ping通服务器检查SSL/TLS配置分析OkHttp日志检查后端服务日志