Bug 55268 - Multidex + ApplicationAttribute = exception application class not found on android 4.4
Summary: Multidex + ApplicationAttribute = exception application class not found on an...
Status: CONFIRMED
Alias: None
Product: Android
Classification: Xamarin
Component: Mono runtime / AOT Compiler (show other bugs)
Version: 7.3 (15.2)
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: dean.ellis
URL:
: 58449 (view as bug list)
Depends on:
Blocks:
 
Reported: 2017-04-17 12:12 UTC by softlion
Modified: 2017-10-07 05:14 UTC (History)
15 users (show)

See Also:
Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Repro project (48.62 KB, application/zip)
2017-04-21 15:35 UTC, softlion
Details
classes.dex file from achanrasmi issue (deleted)
2017-08-28 21:10 UTC, achanrasmi
Details

Description softlion 2017-04-17 12:12:40 UTC
Xamarin 4.4.0.34

When enable multidex is checked AND the android app has an Application derived class (with the ApplicationAttribute), the application will deploy on Android 4.4 but crashes with this exception. I've tryed clean / unchecking multidex without successs.

Only commenting out the Application class will remove the error.


E/AndroidRuntime( 2652): java.lang.RuntimeException: Unable to instantiate application md5cebe7d6f36303c4f9fcb9cc84cb6ddb6.MainApp: java.lang.ClassNotFoundException: Didn't find class "md5cebe7d6f36303c4f9fcb9cc84cb6ddb6.MainApp" on path: DexPathList[[zip file "/data/app/fr.vapolia.radiosfr-2.apk"],nativeLibraryDirectories=[/data/app-lib/fr.vapolia.radiosfr-2, /vendor/lib, /system/lib]]
E/AndroidRuntime( 2652):        at android.app.LoadedApk.makeApplication(LoadedApk.java:516)
E/AndroidRuntime( 2652):        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4317)
E/AndroidRuntime( 2652):        at android.app.ActivityThread.access$1500(ActivityThread.java:135)
E/AndroidRuntime( 2652):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
E/AndroidRuntime( 2652):        at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime( 2652):        at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime( 2652):        at android.app.ActivityThread.main(ActivityThread.java:5017)
E/AndroidRuntime( 2652):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 2652):        at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime( 2652):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
E/AndroidRuntime( 2652):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
E/AndroidRuntime( 2652):        at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 2652): Caused by: java.lang.ClassNotFoundException: Didn't find class "md5cebe7d6f36303c4f9fcb9cc84cb6ddb6.MainApp" on path: DexPathList[[zip file "/data/app/fr.vapolia.radiosfr-2.apk"],nativeLibraryDirectories=[/data/app-lib/fr.vapolia.radiosfr-2, /vendor/lib, /system/lib]]
E/AndroidRuntime( 2652):        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E/AndroidRuntime( 2652):        at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
E/AndroidRuntime( 2652):        at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
E/AndroidRuntime( 2652):        at android.app.Instrumentation.newApplication(Instrumentation.java:975)
E/AndroidRuntime( 2652):        at android.app.LoadedApk.makeApplication(LoadedApk.java:511)
E/AndroidRuntime( 2652):        ... 11 more
Comment 1 softlion 2017-04-18 07:55:30 UTC
I found that the MainApp class is in the second dex file "classes2.dex" instead of classes.dex

Which is incorrect as stated by this blog post: http://www.jon-douglas.com/2016/09/05/xamarin-android-multidex/
Comment 2 softlion 2017-04-18 08:56:46 UTC
I found that the mainDexClasses.bat in build-tools 25.0.2 is never executed.
But the msbuild task does appear.

1>  Output Property: MainDexClassesToolPath=C:\Dev\android-sdk\build-tools\25.0.2\\ (TaskId:64)

...

1>    MultiDexMainDexListFile: obj\Debug\multidex.keep (TaskId:279)
1>    CustomMainDexListFiles: (TaskId:279)
1>      multidex.keep (TaskId:279)
1>    ToolExe: mainDexClasses.bat (TaskId:279)
1>    ToolPath: C:\Dev\android-sdk\build-tools\25.0.2\\ (TaskId:279)
1>    ProguardHome: C:\Dev\android-sdk\tools\proguard\ (TaskId:279)
1>Done executing task "CreateMultiDexMainDexClassList". (TaskId:279)

...

1>  Task Parameter:MultiDexMainDexListFile=obj\Debug\multidex.keep (TaskId:281)

This multidex.keep file is still equal to the content of my custom multidex.keep file instead of being filled automatically.
Comment 3 Atsushi Eno 2017-04-20 04:47:00 UTC
That is the expected behavior. When specifying custom MultiDexMainDexList files it is to NOT run mainDexClasses(.bat) so that things still work when mainDexClasses(.bat) and/or the underlying proguard does not work as expected.
Comment 4 softlion 2017-04-20 05:23:14 UTC
Ok,
But the problem also appear when
no custom multidex file is set.
I can share my screen and mouse.

This is a real problem,
I need help on this one.

I already lost 2 days trying to find
combination of parameters that would work.

But your build system is too closed to
go deeper :(
Comment 5 softlion 2017-04-21 15:35:49 UTC
Created attachment 21702 [details]
Repro project
Comment 6 softlion 2017-04-21 15:39:14 UTC
On ANDROID 4.4 simulator, with VS2017 (and VS2015),
the repro project compiles and deploys fine.

BUT it fails to run with the same exception i have in my own project.
For this repro project, i used a default Android app template, added a couple of nugets, enable multidex so compilation will succeed, then run on an Android 4.4 simulator device.

I/ActivityManager( 1619): Start proc BugAndroidMultidex.BugAndroidMultidex for activity BugAndroidMultidex.BugAndroidMultidex/md520901f8679d79c7e5e8ad9386d27d0b6.MainActivity: pid=3729 uid=10062 gids={50062, 3003, 1028}
D/AndroidRuntime( 3709): Shutting down VM
D/dalvikvm( 3709): GC_CONCURRENT freed 96K, 15% free 578K/676K, paused 1ms+0ms, total 1ms
D/AndroidRuntime( 3729): Shutting down VM
W/dalvikvm( 3729): threadid=1: thread exiting with uncaught exception (group=0x9ccbdb20)
E/AndroidRuntime( 3729): FATAL EXCEPTION: main
E/AndroidRuntime( 3729): Process: BugAndroidMultidex.BugAndroidMultidex, PID: 3729
E/AndroidRuntime( 3729): java.lang.RuntimeException: Unable to instantiate application md520901f8679d79c7e5e8ad9386d27d0b6.MainApp: java.lang.ClassNotFoundException: Didn't find class "md520901f8679d79c7e5e8ad9386d27d0b6.MainApp" on path: DexPathList[[zip file "/data/app/BugAndroidMultidex.BugAndroidMultidex-1.apk"],nativeLibraryDirectories=[/data/app-lib/BugAndroidMultidex.BugAndroidMultidex-1, /vendor/lib, /system/lib]]
E/AndroidRuntime( 3729):        at android.app.LoadedApk.makeApplication(LoadedApk.java:516)
E/AndroidRuntime( 3729):        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4317)
E/AndroidRuntime( 3729):        at android.app.ActivityThread.access$1500(ActivityThread.java:135)
E/AndroidRuntime( 3729):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
E/AndroidRuntime( 3729):        at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime( 3729):        at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime( 3729):        at android.app.ActivityThread.main(ActivityThread.java:5017)
E/AndroidRuntime( 3729):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 3729):        at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime( 3729):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
E/AndroidRuntime( 3729):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
E/AndroidRuntime( 3729):        at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 3729): Caused by: java.lang.ClassNotFoundException: Didn't find class "md520901f8679d79c7e5e8ad9386d27d0b6.MainApp" on path: DexPathList[[zip file "/data/app/BugAndroidMultidex.BugAndroidMultidex-1.apk"],nativeLibraryDirectories=[/data/app-lib/BugAndroidMultidex.BugAndroidMultidex-1, /vendor/lib, /system/lib]]
E/AndroidRuntime( 3729):        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E/AndroidRuntime( 3729):        at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
E/AndroidRuntime( 3729):        at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
E/AndroidRuntime( 3729):        at android.app.Instrumentation.newApplication(Instrumentation.java:975)
E/AndroidRuntime( 3729):        at android.app.LoadedApk.makeApplication(LoadedApk.java:511)
E/AndroidRuntime( 3729):        ... 11 more
W/ActivityManager( 1619):   Force finishing activity BugAndroidMultidex.BugAndroidMultidex/md520901f8679d79c7e5e8ad9386d27d0b6.MainActivity
Comment 7 softlion 2017-04-21 15:40:26 UTC
On an Android 5 simulator this same repro app works fine.
Comment 8 Jon Douglas [MSFT] 2017-04-21 19:47:09 UTC
See the last section of my blog post:

http://www.jon-douglas.com/2016/09/23/xamarin-android-multidex-keep/

What you should do is copy the maindexlist as it gets generated and then add your one class to it.
Comment 9 softlion 2017-04-21 21:09:13 UTC
You can't copy the class list from android-classyshark window.

Atsushi is wrong saying that the custom multidex.keep file overriding completly the tool is expected.
As stated in the Android doc:

"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."

So the multidex.keep classes should be added to those already discovered by the android tool, not override those discovered by the tool.

I finally created this multidex.keep file which seems to work with all Xamarin projects. Hope it will help someone.
Replace the first line with you custom application class. You also need to add the register attribute on your custom application class:
    [Register("bugandroidmultidex.MainApp")]
Note that bugandroidmultidex must match the namespace in which the class is. And replace dots by slashes.

bugandroidmultidex/MainApp.class
android/support/multidex/ZipUtil.class
android/support/multidex/ZipUtil$CentralDirectory.class
android/support/multidex/MultiDex$V14.class
android/support/multidex/MultiDexExtractor$1.class
android/support/multidex/MultiDexExtractor.class
android/support/multidex/MultiDexApplication.class
android/support/multidex/MultiDex.class
android/support/multidex/MultiDex$V19.class
android/support/multidex/MultiDex$V4.class
mono/MonoPackageManager.class
mono/android/app/NotifyTimeZoneChanges.class
mono/MonoPackageManager_Resources.class
mono/android/app/ApplicationRegistration.class
Comment 10 Jonathan Pryor 2017-05-18 15:45:29 UTC
I suspect that this is (partially?) duplicative of Bug #55050.
Comment 11 Jon Douglas [MSFT] 2017-06-22 16:21:57 UTC
Although I've blogged about this item and provided numerous workarounds, I do believe there needs to be some type of logic that takes an inherited "Application" class and puts it on the main dex list keep file. There's been many times where the "Application" class does not make the main dex list and gets put into overfill dex files. This of course causes the crash that softlion is reporting above.

Thus we need a way to add the inherited custom "Application" class to the default multiDexKeepFile that we generate. This is via the 

--main-dex-list=<file>

parameter of the dx tool.

The problem with the override of the Build Action "MultiDexMainDexList" is that you have to take an existing generated multidex.keep to ensure you have all of the glue for Xamarin.Android / Mono included as well. This is very problematic for our users who may not be aware of how to access these classes found in obj\{Configuration}\multidex.keep

Thus I believe this should be an automatic include if an Android application has an inherited Application class defined.

https://developer.android.com/studio/build/multidex.html#keep
Comment 12 Jon Douglas [MSFT] 2017-07-28 14:49:49 UTC
Ontop of the custom "Application" class, we need to also ensure all of the Xamarin.Android "Glue" classes make the main dex list such as:

mono/MonoPackageManager.class
mono/MonoPackageManager_Resources.class
mono/MonoRuntimeProvider.class

mono/android/app/NotifyTimeZoneChanges.class
mono/android/app/ApplicationRegistration.class

This will help prevent users from having to create a custom multidex list and struggling to figure out what these classes do.
Comment 13 Jon Douglas [MSFT] 2017-07-28 14:51:17 UTC
*** Bug 58449 has been marked as a duplicate of this bug. ***
Comment 14 cheansiong 2017-07-29 05:04:51 UTC
*** Bug 58449 has been marked as a duplicate of this bug. ***
Comment 15 Jon Douglas [MSFT] 2017-08-23 16:41:31 UTC Comment hidden (obsolete)
Comment 16 Jon Douglas [MSFT] 2017-08-25 07:45:27 UTC Comment hidden (obsolete)
Comment 24 Brendan Zagaeski (Xamarin Support) 2017-08-28 22:20:16 UTC
The content of attachment 24447 [details] has been deleted for the following reason:

Removing outdated information, as requested by the submitting user.
Comment 25 Alek Slater 2017-09-07 13:21:13 UTC
For the record I'm experiencing the same issue on both 4.4 and 4.1 that I've tested on Mac Visual Studio. And no amount of multidex.keep editing seems to make any difference here, I've found no workaround.
Comment 26 softlion 2017-09-07 15:51:01 UTC
Same issue on Android 5 with Facebook service

Found a workaround: 
Disable multiplex
Enable proguard 

You will need to fix proguard errors
Then you won't need multidex anymore.
Comment 27 Alek Slater 2017-09-07 17:49:51 UTC
Yeah unfortunately that doesn't really work that well for Debug builds. In addition, that may not work for everyone. You can easily end up with more methods than singledex can handle, even with Proguard enabled :(
Comment 28 softlion 2017-09-09 08:51:56 UTC
Alex i can confirm that it does not work in debug mode.

I wanted to make my customer's app available to devices upgraded with lineageos to android 7.1, unfortunatly the Google Play store detects the device model and filter it out so the app is not available on the Google Play store on this model even if the OS have been upgraded.

Plus there is a HUGE bug in the current xamarin release which incorrectly generates the multidex.keep file by forgetting to add carriage returns at the end of each class name, rendering it completly useless.
See bug https://bugzilla.xamarin.com/show_bug.cgi?id=59369
Comment 29 Jonathan ANTOINE 2017-09-28 12:11:39 UTC
Any update on this one ?

This last bug fix make it worst as I am no more able to produce a working application with the .bat file modification : https://bugzilla.xamarin.com/show_bug.cgi?id=38693

Regards
Comment 30 José Fabio Martinez Gonzalez 2017-10-06 13:35:53 UTC
It's fixed for me in the current beta channel with the Xamarin.Android 8.0.0.33 update. Can anyone else confirm please?

https://developer.xamarin.com/releases/android/xamarin.android_8/xamarin.android_8.0/

Note You need to log in before you can comment on or make changes to this bug.