When multidex is enabled in a large Android project building on Android API 28 and using the Android support library’s AppCompatActivity, I am getting the following runtime exception at startup:
java.lang.NoClassDefFoundError: Failed resolution of: Landroid/arch/lifecycle/LifecycleRegistry;
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.arch.lifecycle.LifecycleRegistry" on path: DexPathList[[zip file "/data/app/tests.appcompat-fKCyld1BdeM5gzvnXQo33A==/base.apk"],nativeLibraryDirectories=[/data/app/tests.appcompat-fKCyld1BdeM5gzvnXQo33A==/lib/x86, /system/lib, /vendor/lib]]
That sounds like an extreme edge case, but it’s a pretty major issue. Android apps will now be required to build on API 28, and the support library’s AppCompatActivity
is almost universally used in place of Activity
.
This StackOverflow answer notes that adding the following ProGuard rule fixed the issue, but AFAIK we don’t have access to anything like ProGuard rules in ebuild.
-keep class android.arch.lifecycle.** {*;}
Perhaps more helpfully, the Android documentation seems to address the issue directly (emphasis mine):
When building each DEX file for a multidex app, the build tools perform complex decision-making to determine which classes are needed in the primary DEX file so that your app can start successfully. If any class that’s required during startup is not provided in the primary DEX file, then your app crashes with the error
java.lang.NoClassDefFoundError
.This shouldn’t happen for code that’s accessed directly from your app code because the build tools recognize those code paths, but it can happen when the code paths are less visible such as when a library you use has complex dependencies. For example, if the code uses introspection or invocation of Java methods from native code, then those classes might not be recognized as required in the primary DEX file.
So if you receive
java.lang.NoClassDefFoundError
, then you must manually specify these additional classes as required in the primary DEX file by declaring them with themultiDexKeepFile
or themultiDexKeepProguard
property in your build type. If a class is matched in either themultiDexKeepFile
or themultiDexKeepProguard
file, then that class is added to the primary DEX file.
Based on this, it seems that the issue could be that the android.arch.lifecyle
stuff is a “complex dependency” of AppCompatActivity
.
Strangely, explicitly adding <GradleReference Include="android.arch.lifecycle:runtime:*">
with CopyLocal set to true
does not change anything. The same class is missing from the dex list, even though it is in android.arch.lifecycle:runtime
…
Project: tests.appcompat.zip (4.0 MB)
Build log: t-out.txt (24.9 KB)