在Kotlin中使用Retrofit遇Handshake Failed错误,如何解决?
解决Retrofit发起HTTPS请求时的Handshake Failed错误
问题背景
使用Kotlin+Retrofit调用HTTPS接口时触发Handshake Failed错误,错误信息在onFailure回调中捕获。相关代码及接口信息如下:
MainActivity核心代码
fun getMyData(){ val Base_URL="https://tgju.amirhossein.info/" val retrofitBuilder= Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl(Base_URL) .build() .create(API::class.java) val retrofitData=retrofitBuilder.getData() retrofitData.enqueue(object: Callback<List<MyDataItem>?>{ override fun onResponse( call: Call<List<MyDataItem>?>, response: Response<List<MyDataItem>?> ) { val responseBody=response.body()!! if (response.isSuccessful) { val txt=findViewById<TextView>(R.id.txt) txt.text= responseBody[0].key } } override fun onFailure(call: Call<List<MyDataItem>?>, t: Throwable) { val txt=findViewById<TextView>(R.id.txt) txt.text=t.message // 原代码t.Message为错误写法,Kotlin中应为t.message(小写m) } }) }
API接口定义
interface API { @GET("api/price/currency") fun getData(): Call<List<MyDataItem>> }
数据实体类
data class MyDataItem( val key: String, val price: String, val title: String )
服务器返回示例
[ { "key": "price_dollar_rl", "price": "492,330", "title": "\u062f\u0644\u0627\u0631" }, { "key": "price_eur", "price": "538,800", "title": "\u06cc\u0648\u0631\u0648" } ]
接口地址
https://tgju.amirhossein.info/api/price/currency
解决方法
Handshake Failed本质是SSL/TLS握手验证失败,核心原因是服务器证书不被Android系统信任,以下是针对性解决方案:
1. 先修正代码错误
原代码中onFailure回调里的t.Message是语法错误,Kotlin中Throwable的错误信息属性为**t.message**(小写m),修正后才能正确显示完整错误详情,辅助排查问题。
2. 临时绕过SSL验证(仅测试环境使用)
如果是测试阶段需要快速验证接口功能,可配置Retrofit使用忽略SSL证书验证的OkHttpClient:
// 生成信任所有证书的OkHttpClient private fun createUnsafeOkHttpClient(): OkHttpClient { try { val trustAllCerts = arrayOf<X509TrustManager>(object : X509TrustManager { override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {} override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {} override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf() }) val sslContext = SSLContext.getInstance("SSL") sslContext.init(null, trustAllCerts, SecureRandom()) val sslSocketFactory = sslContext.socketFactory return OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustAllCerts[0]) .hostnameVerifier { _, _ -> true } // 忽略主机名匹配验证 .build() } catch (e: Exception) { throw RuntimeException(e) } }
修改Retrofit构建代码,添加自定义OkHttpClient:
val retrofitBuilder= Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl(Base_URL) .client(createUnsafeOkHttpClient()) // 接入不安全的客户端 .build() .create(API::class.java)
注意:此方法完全关闭SSL安全验证,严禁用于生产环境,会导致数据传输存在被劫持的风险。
3. 导入服务器证书到项目(生产环境推荐)
如果服务器使用自签名证书,正确做法是将证书导入项目,让App信任该证书:
- 将服务器的
.cer格式证书放入项目res/raw目录(无raw目录则新建)。 - 编写代码生成信任该证书的OkHttpClient:
private fun createTrustedOkHttpClient(context: Context): OkHttpClient { try { val certificateFactory = CertificateFactory.getInstance("X.509") val inputStream = context.resources.openRawResource(R.raw.your_certificate) // 替换为你的证书文件名 val certificate = certificateFactory.generateCertificate(inputStream) inputStream.close() val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) keyStore.load(null, null) keyStore.setCertificateEntry("server", certificate) val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm() val tmf = TrustManagerFactory.getInstance(tmfAlgorithm) tmf.init(keyStore) val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, tmf.trustManagers, SecureRandom()) return OkHttpClient.Builder() .sslSocketFactory(sslContext.socketFactory, tmf.trustManagers[0] as X509TrustManager) .build() } catch (e: Exception) { throw RuntimeException(e) } }
- 在Retrofit构建时接入该客户端:
val retrofitBuilder= Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl(Base_URL) .client(createTrustedOkHttpClient(this)) // 传入当前Context .build() .create(API::class.java)
4. 检查基础配置
- 确保
AndroidManifest.xml中添加了网络权限:
<uses-permission android:name="android.permission.INTERNET" />
- 通过浏览器访问接口地址,确认服务器SSL证书未过期、域名与证书匹配。
内容的提问来源于stack exchange,提问作者Hos 8990
相关产品推荐
相关产品推荐

