升级Chromium 136至Bundle APK时PlaybackListenerService崩溃(dexPath==null)
Chromium 136 Bundle APK 崩溃问题排查与修复
问题描述
将项目升级至Chromium 136后,本地发布APK运行正常,但通过Play商店或bundletool安装的Bundle构建版本启动时崩溃,报错如下:
FATAL EXCEPTION: main java.lang.RuntimeException: Unable to create service org.chromium.chrome.browser.media.ui.ChromeMediaNotificationControllerServices$PlaybackListenerService: java.lang.NullPointerException: dexPath == null at android.app.ActivityThread.handleCreateService(ActivityThread.java:5391) ... Caused by: java.lang.NullPointerException: dexPath == null at dalvik.system.DexPathList.<init>(DexPathList.java:144) at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:160) at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:45) at org.chromium.base.BundleUtils.d(BundleUtils.java:62) at org.chromium.base.BundleUtils.g(BundleUtils.java:29) at org.chromium.base.BundleUtils.h(BundleUtils.java:26) at org.chromium.chrome.browser.base.SplitCompatUtils.loadClassAndAdjustContext(SplitCompatUtils.java:1) at org.chromium.chrome.browser.base.SplitCompatService.attachBaseContext(SplitCompatService.java:3) at android.app.Service.attach(Service.java:983)
已尝试的方案:
- 在ProGuard/R8规则中保留相关服务实现类:
-keep,allowobfuscation class org.chromium.chrome.browser.media.ui.ChromeMediaNotificationControllerDelegate$PlaybackListenerServiceImpl { *; } -keep,allowobfuscation class org.chromium.chrome.browser.media.ui.ChromeMediaNotificationControllerDelegate$PresentationListenerServiceImpl { *; } -keep,allowobfuscation class org.chromium.chrome.browser.media.ui.ChromeMediaNotificationControllerDelegate$CastListenerServiceImpl { *; } - 强制
BundleUtils.isBundle()返回false规避崩溃,但属于临时 workaround,可能破坏拆分逻辑:@CalledByNative public static boolean isBundle() { return false; // temporary workaround }
问题解答
1. 为何仅Bundle APK会出现dexPath == null错误,本地发布APK却正常?
普通发布APK是单包结构,所有Dex文件都打包在主APK中,类加载器可以直接获取明确的Dex路径,不需要处理动态模块的拆分逻辑。而Bundle APK采用**应用拆分(App Bundle)**机制,会将代码、资源拆分为多个动态模块,Chromium的SplitCompat框架会在运行时尝试加载对应模块的Dex文件。
Chromium 136的SplitCompat逻辑触发空指针的核心原因:
- 动态模块的声明或打包配置错误,导致模块未被正确包含或安装;
BundleUtils中获取模块Dex路径的逻辑未处理模块未安装或路径不存在的场景;- Bundle安装时,动态模块的加载时机与服务启动时机不匹配,服务启动时模块尚未完成安装。
2. Chromium 136动态特性下SplitCompat崩溃的正确修复方案
方案1:修复BundleUtils中的Dex路径获取逻辑
修改org.chromium.base.BundleUtils中创建模块类加载器的代码,添加dexPath非空校验,避免空指针:
// 对应BundleUtils.d()方法的逻辑调整 public static ClassLoader createModuleClassLoader(String moduleName, Context context) { String dexPath = getModuleDexPath(moduleName, context); // 当dexPath为空时,回退到应用主类加载器 if (dexPath == null) { return context.getClassLoader(); } // 原有的类加载器创建逻辑 return new PathClassLoader(dexPath, context.getClassLoader()); }
方案2:完善ProGuard/R8规则
除了保留服务实现类,还需要保留SplitCompat相关的核心类和方法,避免混淆导致逻辑异常:
// 保留BundleUtils核心方法 -keep class org.chromium.base.BundleUtils { *; } // 保留SplitCompat工具类与服务类 -keep class org.chromium.chrome.browser.base.SplitCompatUtils { *; } -keep class org.chromium.chrome.browser.base.SplitCompatService { *; } // 保留崩溃相关的服务类 -keep class org.chromium.chrome.browser.media.ui.ChromeMediaNotificationControllerServices$PlaybackListenerService { *; }
方案3:检查动态模块配置
确认项目中动态模块的声明正确:
- 在
settings.gradle中确保动态模块已被包含:include ':app', ':media_ui' // 假设media_ui是相关动态模块 - 在主模块的
build.gradle中声明动态特性依赖:android { dynamicFeatures = [':media_ui'] }
方案4:调整SplitCompatService的模块加载逻辑
在SplitCompatService的attachBaseContext方法中,添加模块存在性校验,确保模块已安装后再加载类:
@Override protected void attachBaseContext(Context base) { String moduleName = getModuleName(); // 根据服务所属模块获取名称 if (BundleUtils.isModuleInstalled(moduleName, base)) { super.attachBaseContext(SplitCompatUtils.loadClassAndAdjustContext(base, getClass())); } else { super.attachBaseContext(base); } }
内容的提问来源于stack exchange,提问作者Sanjay Bhalani
相关产品推荐
相关产品推荐

