java.lang.NoClassDefFoundError at runtime when multidex is enabled on Android 28

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 the multiDexKeepFile or the multiDexKeepProguard property in your build type. If a class is matched in either the multiDexKeepFile or the multiDexKeepProguard file, then that class is added to the primary DEX file.

https://developer.android.com/studio/build/multidex.html#keep

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)

Curious. I’ll look into that and see if its a big or or we indeed need to expose a setting for multiDexKeepFile .

I assume the same project builds and runs fine using Incremental or Predex, correct?

1 Like

Thanks, logged as bugs://82934

It’s set to PreDex in the current project, I just also have Multidex enabled.

Ah right, forgot that thats a separate option, not an alternative to Pre/Inc. But it only happens with MultiDex on, correct?

From what I can tell, yes. It also only happens if it needs to Multidex (method count >65k). I included the Dropbox SDK in my test project as a cheap way of bloating the method count to force Multidex.

1 Like

Has this been fixed? I’m on .2422 and was going to test something related to this, but it appears that the exact test project I uploaded here is now building and running without issue.

I haven’t touched anything on this front yet, no. Odd that it works now, maybe some unrelated compiler-side fix affected it, iirc a couple Java issues got fixed.

Should I close the issue, or do you wanna do more re-testing?

1 Like

You can close it. I actually noticed this initially because my main project appears to work now on API 28, so this project working is already a re-test of sorts.

1 Like

bugs://82934 got closed with status nochangereq.

For anyone finding this thread in the future, I started getting a ClassNotFoundException on android.arch stuff again on API 28, this time unrelated to multidex. In this case it seemed to be triggered whenever I would try to load a fragment into a view.

Regardless, I manually refactored all of the Android support library packages in my project over to AndroidX, and everything now works.

Here are two simple test cases that attempt to replace part of a view with a new fragment on startup. The projects are identical except that the first uses the old android.support.* namespace (and crashes) and the second uses the androidx.* namespace (and works).

tests.appcompat.zip (14.6 KB)
tests.androidx.zip (14.5 KB)