一、报错描述

迁移 Java Sample 到 Android 项目中,使用 org.apache.http 库运行时报错如下:

FATAL EXCEPTION: pool-2-thread-1 (Ask Gemini)
Process: com.xxx.devicemanager, PID: 18821
java.lang.NoSuchFieldError: No static field INSTANCE of type Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier; in class Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier; or its superclasses (declaration of 'org.apache.http.conn.ssl.AllowAllHostnameVerifier' appears in /system/framework/framework.jar!classes5.dex)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.<clinit>(SSLConnectionSocketFactory.java:151)
at org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder.java:981)
at org.apache.http.impl.client.HttpClients.createDefault(HttpClients.java:56)
at com.xxx.devicemanager.utils.HttpClientUtils.doPostJson(HttpClientUtils.java:200)
at com.xxx.devicemanager.CloudLinkManager$1.lambda$onSuccess$0$com-xxx-devicemanager-CloudLinkManager$1(CloudLinkManager.java:195)
at com.xxx.devicemanager.CloudLinkManager$1$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012) 

使用 org.apache.httpcomponents:httpclient 4.5.14,当Android Studio 编译时候也会报错,添加如下编译通过

android {
    ...
    packaging {
        exclude 'META-INF/DEPENDENCIES'
    }
    ...
}

二、报错分析

这个问题是一个典型的在 Android 开发中遇到的库冲突问题。错误日志 java.lang.NoSuchFieldError: No static field INSTANCE of type Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier; 表明,您的代码在运行时加载的 AllowAllHostnameVerifier 类版本中,并不存在一个名为 INSTANCE 的静态字段。

这个问题的根本原因在于 库版本冲突。具体来说:

  1. Android 系统自带了 Apache HttpClient 库:为了历史兼容性,Android 系统本身在 framework.jar 中内置了一个版本的 Apache HttpClient 库。然而,这个系统内置的版本通常比较旧。
  2. 您的应用或其依赖的库引入了新版本的 HttpClient:您的应用(com.xxx.devicemanager)或者它所依赖的某个库(例如某个网络请求库)也引入了一个较新版本的 Apache HttpClient。
  3. 冲突发生:在运行时,Java 虚拟机(JVM)加载了 Android 系统提供的旧版本 AllowAllHostnameVerifier 类。从错误信息中的路径 /system/framework/framework.jar!classes5.dex 可以确认这一点。然而,您的代码(具体是在 org.apache.http.conn.ssl.SSLConnectionSocketFactory.<clinit>)是基于新版 HttpClient 编译的,它期望 AllowAllHostnameVerifier 类中有一个 INSTANCE 静态字段。由于加载的是没有这个字段的旧版本类,因此抛出了 NoSuchFieldError 异常。

这个问题在 Android 6.0 (API 23) 及以上版本尤为常见,因为从这个版本开始,Google 不再推荐使用 Apache HTTP 客户端,并对其支持做了变更。

三、解决方案

使用专门为 Android 优化的 HttpClient 库(推荐)

最直接且推荐的解决方法是,使用一个已经处理了这类冲突的第三方库,这个库会将 Apache HttpClient 的类重新打包到一个新的命名空间下,从而避免和系统自带的库冲突。

1.在您项目的 build.gradle (Module: app) 文件中,添加以下依赖:

dependencies {
    // ... 其他依赖
    implementation 'cz.msebera.android:httpclient:4.5.8' // 或者更新的版本
}

2.修改您的代码,将所有 org.apache.http 的 import 语句修改为 cz.msebera.android.httpclient。例如:

  • import org.apache.http.HttpResponse; -> import cz.msebera.android.httpclient.HttpResponse;
  • import org.apache.http.client.HttpClient; -> import cz.msebera.android.httpclient.client.HttpClient;

这种方法的优点是能够彻底解决命名空间冲突的问题。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐