# Android 4.2 兼容性改动文档 ## 背景 项目 original 目标为 Android 5.0+,需降级兼容 Android 4.2(API 17)。Android 4.2 使用 Dalvik 虚拟机(非 ART),对 multidex 和高版本 API 引用有严格限制,导致一系列兼容性问题。 ## 问题分类 兼容性问题分为两大类: 1. **Dalvik multidex 类加载失败**:Dalvik 在框架级反射和内部类加载场景下,无法从 secondary dex 加载类,导致 `ClassNotFoundException` / `NoClassDefFoundError` / `VerifyError` 2. **高版本 API 引用**:第三方库的某些类引用了 Android 10+ 或 Java 9+ 的 API,Dalvik 验证器拒绝加载,导致 `VerifyError` --- ## 一、Multidex 配置改动 ### 1.1 multidex-config.pro(主 dex 保留类) **文件**:`app/multidex-config.pro` **原因**:Dalvik multidex 环境下,以下场景无法从 secondary dex 加载类: - `LayoutInflater.createViewFromTag`(框架级反射创建 View) - `AppCompatDelegateImpl.createSubDecor`(inflate 内部装饰布局) - 协程内部机制加载 `CoroutineExceptionHandlerImplKt` - Retrofit `KotlinExtensions.await()` 被 `HttpServiceMethod` 运行时调用 **保留到主 dex 的包**: ``` - com.pukka.mediaapp.** # 项目自身所有类 - androidx.appcompat.app.** # AppCompatDelegateImpl、AppCompatViewInflater - androidx.appcompat.widget.** # FitWindowsLinearLayout、AppCompatButton 等 - kotlinx.coroutines.** # 协程核心类 - retrofit2.** # KotlinExtensions、HttpServiceMethod 等 ``` ### 1.2 AndroidManifest.xml **文件**:`app/src/main/AndroidManifest.xml` **改动**: - 移除 `com.google.android.exoplayer2` 的 `overrideLibrary`(已移除 ExoPlayer 依赖) - 保留其他 AndroidX 库的 `overrideLibrary`(core-ktx 1.12.0、appcompat 1.6.1 等的 minSdk 为 19-21,通过 override 覆盖为 17) - 移除 `androidx.startup.InitializationProvider`(Android 4.x Dalvik multidex 下 Provider 启动时无法加载 secondary dex 中的初始化类,App 手动初始化所有组件) --- ## 二、第三方库版本降级 ### 2.1 OkHttp:3.14.9 → 3.12.0 **问题**:OkHttp 3.12.1+ 包含 `Android10Platform` 类,引用 Android 10+ 的 SSL API(`SSLParameters.setApplicationProtocols`)。`Platform.findAndroidPlatform()` 在静态初始化时尝试加载该类,Dalvik 验证器拒绝 → `VerifyError` **修复**: ```groovy implementation 'com.squareup.okhttp3:okhttp:3.12.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0' // 防止其他依赖传递引入 3.12.1+ configurations.all { resolutionStrategy.force 'com.squareup.okhttp3:okhttp:3.12.0' } ``` ### 2.2 Gson:2.10.1 → 2.8.6 **问题**:Gson 2.9+ 引入 `ReflectionHelper` 类,引用 Java 9+ 的反射 API(`java.lang.invoke.MethodHandles`)。`ReflectiveTypeAdapterFactory.create()` 调用该类,Dalvik 验证器拒绝 → `VerifyError` **修复**: ```groovy implementation 'com.google.code.gson:gson:2.8.6' ``` ### 2.3 Retrofit:2.9.0 → 2.6.0 **问题**:Retrofit 2.9.0 传递依赖 OkHttp 3.14.9(含 `Android10Platform`)。与客户环境保持一致使用 2.6.0。 **修复**: ```groovy implementation 'com.squareup.retrofit2:retrofit:2.6.0' implementation 'com.squareup.retrofit2:converter-gson:2.6.0' ``` ### 2.4 Kotlin Coroutines:1.7.3 → 1.6.4 **问题**:kotlinx-coroutines 1.7+ 要求 minSdk 21(Android 5.0),1.6.4 兼容 API 16+。 **修复**: ```groovy implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' ``` --- ## 三、ExoPlayer 移除,改用系统原生 MediaPlayer **问题**:ExoPlayer 2.14+ 要求 minSdk 21,2.19.1 的类大量引用 Android 5+ API,在 Android 4.2 上会 `VerifyError`。即使降到 2.13.0,API 差异大且仍有兼容性风险。 **修复**:完全移除 ExoPlayer,改用 Android 系统原生 `MediaPlayer` + `SurfaceView`(API 1+,零兼容性问题)。 **涉及文件**: | 文件 | 改动 | |------|------| | `app/build.gradle` | 移除 `exoplayer:2.19.1` 依赖 | | `app/src/main/AndroidManifest.xml` | 移除 `com.google.android.exoplayer2` overrideLibrary | | `app/src/main/res/layout/activity_player.xml` | `PlayerView` → `SurfaceView` | | `app/src/main/java/.../ui/VideoPlayerActivity.kt` | 重写为 `MediaPlayer` + `SurfaceHolder.Callback` | --- ## 四、主题改动 **文件**:`app/src/main/res/values/themes.xml` **改动**:`Theme.MaterialComponents.NoActionBar` → `Theme.AppCompat.NoActionBar` **原因**: - `MaterialComponentsViewInflater` 继承 `AppCompatViewInflater`,被框架反射加载时需在主 dex - 保留整个 `com.google.android.material.**` 到主 dex 会导致方法数超限(80949 > 65536) - 布局中只用了标准控件(TextView/EditText/Button/ProgressBar),无需 Material 主题 - 改用 AppCompat 主题后,使用 `AppCompatViewInflater`,避免 Material 的 multidex 问题 --- ## 五、build.gradle 完整依赖对照 | 依赖 | 修复前 | 修复后 | 原因 | |------|--------|--------|------| | retrofit | 2.9.0 | **2.6.0** | 传递依赖 OkHttp 3.14.9 不兼容 | | converter-gson | 2.9.0 | **2.6.0** | 同上 | | okhttp | 3.14.9(传递) | **3.12.0**(force) | Android10Platform VerifyError | | logging-interceptor | 3.12.12 | **3.12.0** | 同上 | | gson | 2.10.1 | **2.8.6** | ReflectionHelper VerifyError | | coroutines | 1.7.3 | **1.6.4** | 1.7+ 要求 minSdk 21 | | exoplayer | 2.19.1 | **移除** | minSdk 21,改用 MediaPlayer | | appcompat | 1.6.1 | 1.6.1(不变) | overrideLibrary 覆盖 | | core-ktx | 1.12.0 | 1.12.0(不变) | overrideLibrary 覆盖 | | material | 1.11.0 | 1.11.0(不变) | 改用 AppCompat 主题 | | lifecycle | 2.3.1 | 2.3.1(不变) | 兼容 API 16+ | | activity | 1.3.1 | 1.3.1(不变) | 兼容 API 16+ | | multidex | 2.0.1 | 2.0.1(不变) | 兼容 API 16+ | --- ## 六、问题排查记录(按发现顺序) | 序号 | 错误 | 根因 | 修复 | |------|------|------|------| | 1 | `VerifyError: AppCompatViewInflater` | Dalvik multidex 无法加载 secondary dex 的 AppCompat 类 | multidex-config.pro 保留 `androidx.appcompat.**` | | 2 | `ClassNotFoundException: FitWindowsLinearLayout` | AppCompat 内部 widget 不以 `AppCompat` 开头,通配符未覆盖 | 改用包级别 `androidx.appcompat.widget.**` | | 3 | `NoClassDefFoundError: CoroutineExceptionHandlerImplKt` | 协程内部类加载失败 | multidex-config.pro 保留 `kotlinx.coroutines.**` | | 4 | `VerifyError: Android10Platform` | OkHttp 3.12.1+ 引用 Android 10+ API | OkHttp 降级 3.12.0 + force 锁定 | | 5 | `VerifyError: ReflectionHelper` | Gson 2.9+ 引用 Java 9+ API | Gson 降级 2.8.6 | | 6 | `NoClassDefFoundError: retrofit2.KotlinExtensions` | Retrofit suspend 支持类在 secondary dex | multidex-config.pro 保留 `retrofit2.**` | | 7 | 主 dex 方法数超限 80949 > 65536 | 保留 material.** 到主 dex | 改用 AppCompat 主题,移除 material 保留 |