Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Espresso multidex fail

We use multidex in our app for a long time but recently with latest update it fails on android API <19 e.g. emulator with api 16 It is standard java.lang.NoClassDefFoundError.

If I define multidexKeepProguard for missing class e.g. java.lang.NoClassDefFoundError. rx.plugins.RxJavaHooks exception

-keep class rx.plugins.**{*;}

then it will just fail in a different place with the same reason NoClassDefFound

Here is the runner, app and manifest setup:

https://gist.github.com/originx/1890599b57b0ee3e14a85a4732301cd9

Logcat:

https://gist.github.com/originx/887f80d405334f1903b3024eb5cd1024

Build enviroment setup:

Android Studio 2.2.2 Build #AI-145.3360264, built on October 18, 2016 JRE: 1.8.0_112-release-b05 x86_64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o

Compile options

compile 'com.android.support:multidex:1.0.1'

build tools info:

   classpath 'com.android.tools.build:gradle:2.2.2'
   compileSdkVersion 25
   buildToolsVersion '25'


   defaultConfig {
        applicationId "app.packagename.com"

         minSdkVersion 16
        targetSdkVersion 25

        testInstrumentationRunner "de.payback.app.CustomAndroidJUnitRunner"
        multiDexEnabled true
    }

 dexOptions {
        jumboMode true
        preDexLibraries false
        javaMaxHeapSize "4g"
        maxProcessCount = 8
    }

  debug {
            applicationIdSuffix '.debug'
            versionNameSuffix '-debug'
            signingConfig signingConfigs.debug
            minifyEnabled false
            shrinkResources debugShrinkResourcesEnabled
            proguardFiles getDefaultProguardFile('proguard-android.txt'), '../proguardRules/proguard-rules.pro', '../proguardRules/proguard-debug-rules.pro'
          //  multiDexKeepProguard file('../proguardRules/multidex-proguard.pro')
            testProguardFiles getDefaultProguardFile('proguard-android.txt'), '../proguardRules/proguard-rules.pro', '../proguardRules/proguard-debug-test-rules.pro'
            testCoverageEnabled false
        }
        release {
            minifyEnabled true
            shrinkResources true
            testProguardFiles getDefaultProguardFile('proguard-android.txt'), '../proguardRules/proguard-rules.pro'
            proguardFiles getDefaultProguardFile('proguard-android.txt'), '../proguardRules/proguard-rules.pro'
          //  multiDexKeepProguard file('../proguardRules/multidex-proguard.pro')
        }

I tried everything from extending MultiDexApplication, to custom MultiDex.install(context) to using MultiDexRunner

same results always

if using multidexkeepproguard file for classes which are usually not found then they are in main dex file but of course something else is missing which indicates that multidex was not properly installed and initialized

Google bug report:

https://code.google.com/p/android/issues/detail?id=228449

repo to reproduce the issue can be found here:

https://github.com/originx/multidex/tree/master

To run please disable instant run

To reproduce multidex issue please run following command

./gradlew clean connectedPayGermanyCompatDebugAndroidTest

run on any device or API 16 emulator Tests on GTI8190 4.1.2 failed Instrumentation run failed due to java.lang.NoClassDefFoundError

Any suggestions how to work around this until I get more info from the Google team?

like image 468
originx Avatar asked Nov 29 '16 13:11

originx


People also ask

What is multidex enabled?

Android applications by default have SingleDex support which limits your application to have only 65536 methods(references). So multidexEnabled = true simply means that now you can write more than 65536 methods(references) in your application.

What is the maximum number of methods supported by DEX compiler before needing to enable multidex?

The Dalvik Executable specification limits the total number of methods that can be referenced within a single DEX file to 65,536—including Android framework methods, library methods, and methods in your own code.

Why use multidex in Android?

By adding this library, your app can manage the access of additional DEX files. In other words, if you are having more than 64K methods, then you will be having more than one DEX file and these DEX files will be managed by using this multidex support library.


1 Answers

Explanation by Google dev:

The issue is that the rx.plugins.RxJavaHooks class referenced from the CustomJunitRunner.onCreate() method is in the secondary dex file of the main app, and you are accessing it before the class loaders get fully patched.

When the main application and test code share a dependency, we will remove it from the test's dependencies (as we expect it to be available in the main application). However, with legacy multidex, this is causing problems.

Currently, there are 2 workarounds:

Option 1 Ensure the rx.plugins.RxJavaHooks is in the main dex by creating a file multidexKeepProguard.pro and adding "-keep class rx.plugins.**"

Option 2 Remove references to RxJavaHooks from onCreate(), and move them to onStart() (not sure if this accomplishes when you want though): @Override public void onStart() { super.onStart(); //hook up schedulers to rxjava so espresso idling resouces can fetch it properly RxJavaHooks.setOnComputationScheduler(current -> Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR)); RxJavaHooks.setOnIOScheduler(current -> Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR)); RxJavaHooks.setOnNewThreadScheduler(current -> Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR)); }

Solution

Workaround

So current workaround would be either use multidexKeepProguard.pro file and in your debug config point to that file:

 debug {
            applicationIdSuffix '.debug'
            multiDexKeepProguard file('../proguardRules/multidex-proguard.pro')
        }

Your multidex proguard file should contain classes which are not being found in the main dex file, in my case it was RxJavaPlugin, so my multidexproguard file contains:

-keep class rx.** { *; }
like image 113
originx Avatar answered Oct 23 '22 19:10

originx