在过去,Android 上发送 HTTP 请求一般有两种方式,一种是 HttpURLConnection
,另一种是 HttpClient
。
不过由于 HttpClient
存在 API 数量过多、扩展困难等特点,Android 团队越来越不建议我们使用这种方式,从 Android 2.3(Gingerbread,API Level 9)开始,效率更高的 HttpURLConnection
便成了主流,HttpURLConnection
可以通过透明压缩和响应缓存减少网络使用,并可最大限度降低耗电量。
终于在 Android M(Marshmallow,Android 6.0,API Level 23)中,HttpClient
的功能被完全移除,标志着此功能被正式弃用,所以现在 HttpURLConnection
便是 Android SDK 中你能见到的唯一网络请求 API。
而到了 Android P(Pie,Android 9,API Level 28),Android 官方再下重手,将 HttpClient
从 bootclasspath 中移除且默认情况下应用无法使用它。
但现实情况是,仍然有许多老项目依然在运行着 HttpClient
,维护这些老项目的开发人员往往因为各种原因而没有对其进行重构,甚至无法对其进行重构,因此这台破旧的老爷车依然在吭呲吭呲地行驶着。
如果你尝试将使用 HttpClient
的老项目的 targetSdkVersion
指定到 28
或以上,那么你在进行网络请求时,很可能会遇到卡死的情况,项目会抛出类似如下异常:
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{<packageName>/<classpath>}: java.lang.RuntimeException: Stub!
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3782)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3961)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2386)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:8178)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
Caused by: java.lang.RuntimeException: Stub!
at org.apache.http.conn.scheme.SchemeRegistry.<init>(SchemeRegistry.java:5)
at ...
其实这个堆栈信息很具迷惑性,会让你误以为是逻辑问题、混淆错误、类库错用等等的问题,但实际上只是因为 Android P 将 HttpClient
从 bootclasspath 中移除所产生的错误罢了。
跟之前『Android P 解除 HTTP 明文通信限制』类似,Android 官方依然给我们留了后门,只需在 AndroidManifest.xml
添加以下内容即可:
<manifest ...>
<application ...>
...
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
</application>
</manifest>
接下来你就可以继续开着这台老爷车吭呲吭呲地上路了。