I have an application that has been using multidexing successfully already, but now I have a framework that needs to wrap itself around the main classes.dex, which means it needs to be below the max references limit after wrapping it.
But no matter what Gradle dexOptions I provide, the main classes.dex ALWAYS contains the max 65535 references:
The Gradle settings I have / have tried:
dexOptions { // params tried separately and together
additionalParameters '--minimal-main-dex --set-max-idx-number=55000'
}
defaultConfig {
multiDexEnabled true
// Shouldn't be necessary for source code within module
multiDexKeepFile file('multidex-config.txt')
}
With the multidex-config.txt keep-file containing ONLY my Application class which is pretty small: com/package/name/MyApplication.class
Other info:
minSdkVersion 21
targetSdkVersion 26
Total dex files:
Am I missing something?
EDIT: Okay so I've just figured out that for some reason it works with my 'qa' buildType sometimes, but not on my 'debug' buildType, and also only works on 'qa' on my local machine, not on others including our build server. I run a gradle clean before every build, so it can't be caching that's the problem.
buildTypes {
debug { // DOESN'T WORK
testCoverageEnabled false
zipAlignEnabled false
applicationIdSuffix ".debug"
}
qa { // DOES WORK, sometimes, on my local machine only
initWith(debug)
applicationIdSuffix ".qa"
zipAlignEnabled false
testCoverageEnabled false
}
release { // ALWAYS WORKS - Probably due to minification by Proguard
zipAlignEnabled false // To be signed later
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
Still have no idea why that's happening.
Add following dependency in your gradle file.
dependencies {
...
implementation 'com.android.support:multidex:1.0.2'
}
and also update dexOptions with following code
dexOptions {
javaMaxHeapSize "4g"
}
Related
My Android project has multiple build type and productFlavors
flavorDimensions "default"
productFlavors {
favor1 {
applicationId "com.abc.android"
versionCode 1
versionName "1"
}
flavor2 {
applicationId "com.abc.android"
versionCode 1
versionName "2"
}
}
buildTypes {
staging {
debuggable true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
develop {
applicationIdSuffix ".develop"
debuggable true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
I have placed my file, say(Abc.java) under each of flavor1staging, flavor1develop, flavor2staging, flavor2develop directory
I can do ./gradlew assemblefalor1staging on my local Android studio and it works fine but when run using travis it cannot map the file and gives me error Unresolved reference Abd
Similarly for any string resources that are defined in the flavorbuild folders but not in the main folder
I have a question does the build is working fine if you give it only 1 build type?
Because according to Travis-CI they do not mention about supporting multiple build types in this link
https://docs.travis-ci.com/user/languages/android/
As all builds in the example containing only one build as you can see in these examples
https://github.com/andrewhr/rxjava-android-example/blob/master/app/build.gradle
https://github.com/pestrada/android-tdd-playground/blob/master/app/build.gradle
Please follow these projects if the project working fine after you make build.gradle as it is shown in example then add multiple builds if then gives issues need to check also log.trace of android & maybe not supported.
make it simple then complex the solution so that you could find where is the error.
One of my test phone does not able to install app with ADB when the application class is extending MultiDex application.
I attempted implement this on build.gradle with my app application class extending MultiDexApplication but cannot compile.
buildTypes {
release {
multiDexEnabled false
}
debug {
multiDexEnabled true
}
}
References:
1. https://developer.android.com/studio/build/multidex.html
2. Can I enable multidex in Android debug build only?
3. Conditionally inherit from class
Keep your Application class extend MultidexApplication, but add this line into your attachBaseContext
if (BuildConfig.DEBUG) {
MultiDex.install(this);
}
Maybe I can suggest a different approach.
I assume you are using proguard on your release build and this is why you don't need to use multidex for release
If you set minifyEnabled true and useProguard false on debug, this will strip out all the dead code in your debug build but wont use any of the proguard optimizations like obfuscation.
You will need to move your proguard rule settings into default config so minify knows which files to keep.
This method can help you avoid using multidex on your debug builds.
defaultConfig {
...
proguardFile 'proguard-rules.pro'
proguardFiles getDefaultProguardFile('proguard-android.txt')
}
debug {
...
minifyEnabled true
useProguard false
}
release {
...
minifyEnabled true
}
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?
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.** { *; }
I'm using proguard to reduce my apk size. The debug apk reduce from 90mb to 55mb, but the signed apk is 71mb. Here is my build.gradle code:
apply plugin: 'com.android.application'
android {
signingConfigs {
XXXX {
keyAlias 'xxxx'
keyPassword 'xxxx'
storeFile file('/Users/xxxx.jks')
storePassword 'xxxxxx'
}
}
compileSdkVersion 23
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.xxxx"
minSdkVersion 14
targetSdkVersion 22
versionCode 61
versionName "4.1.8.1"
multiDexEnabled true
signingConfig signingConfigs.XXXX
ndk {
abiFilters "armeabi", "armeabi-v7a", "x86", "mips"
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.XXXX
}
debug {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.XXXX
}
}
productFlavors {
}
dexOptions {
javaMaxHeapSize "4g"
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
}
}
and
repositories {
mavenLocal()
maven {
name "jcenter"
url "http://jcenter.bintray.com/"
}
}
dependencies {
...
}
Further explaining sosite's answer, it seems that this happens only if comparing a debug apk built via Run or Debug meant for a specific device (even without Instant Run enabled) instead of a debugapk built via Build > Build APK (for any supported device).
Any variant (even debug itself) built via Build APK will include all the resources for that variant. Also, the Run/Debug apk includes pre-dexed classes specific for that single device, while Build APK ones includes only some general pre-dexed classes that the compiler determines safe for all supported devices - the full dexing only occurs in the device itself, when the apk is installed.
I've zipdiff-ed an apk generated via Debug with another via Build APK for the same variant of the same project and published the simplified output for demonstration (also available as html).
When you build your app locally for specific type of phone then Android Studio attach only necessary resource files. When you build release version then you have attached all types of drawables so you app file size can increase drastically.
I suggest you to use jpg in place of png in as many places as you can and compress them of course - often I use tinyPNG website or just Photoshop ;)
I activated the resouce shrinking in my build.gradle but now my embeded wearable app is stripped out. How can I avoid that my micro app is removed, because it is unused?
Skipped unused resource res/raw/android_wear_micro_apk.apk: 382310 bytes
Since I want to shrink the other not used resouces I'm using this DSL:
buildTypes {
release {
shrinkResources true
// ...
}
}
I would guess that I need to use proguard but I have no idea how to achieve that. I checked of cause the documentation, but I didn't get it how protect a single member variable.
Are you referencing the R.raw.apkpath? Looking at the Packaging Wearable Apps training mentions rawPathResId in the res/xml/wearable_app_desc.xml
On a side note enabling proGuard is simple with Gradle
buildTypes {
release {
runProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
This is Bug 78620 and was fixed in the gradle build tools 0.14.1.