As part of the Android Studio 2.2 roll out I updated my Gradle Build tools to v2.2. After doing that my signed APK build process fails because I have shrinkResources = true.
Once I switch back to Gradle v2.1.3 OR set shrinkResources = false everything works fine. Here's my app gradle build file:
android {
signingConfigs {
}
compileSdkVersion 24
buildToolsVersion '24.0.0'
defaultConfig {
applicationId "com.sample.testapp"
minSdkVersion 21
targetSdkVersion 24
versionCode 4
versionName "0.0.4"
}
buildTypes {
release {
minifyEnabled false
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFile 'C:/Users/code/testapp/app/proguard-rules.pro'
}
}
productFlavors {
}
}
With Gradle set to v2.2 here's the build error I get when generating a signed APK
Does anyone know why this is occurring and if there's a work around? I've Googled around a bit and have seen some older Android Bug reports about alpha and beta Gradle builds having this issue, but the reports I found were >6 months old (and for previous Gradle versions).
P.S. I know that minifyEnabled = false currently, i have yet to investigate the correct set of proguard rules for my included libraries to prevent the Signed Build from erroring out due to missing libs.
To use shrinkResources you have to use minifyEnabled
As per Android documentation:
Resource shrinking works only in conjunction with code shrinking.
After the code shrinker removes all unused code, the resource shrinker
can identify which resources the app still uses. This is especially
true when you add code libraries that include resources—you must
remove unused library code so the library resources become
unreferenced and, thus, removable by the resource shrinker.
To enable resource shrinking, set the shrinkResources property to true
in your build.gradle file (alongside minifyEnabled for code
shrinking).
use
minifyEnabled false
shrinkResources false
or
minifyEnabled true
shrinkResources true
may be a bug on android gradle plugin
wait google fix bugs
Related
I have created a multidex app. But in regards to proguard i have the following in build.gradle:
android {
defaultConfig {
...
multiDexEnabled true
}
productFlavors {
dev {
// Enable pre-dexing to produce an APK that can be tested on
// Android 5.0+ without the time-consuming DEX build processes.
minSdkVersion 21
}
prod {
// The actual minSdkVersion for the production version.
minSdkVersion 14
}
}
buildTypes {
release {
minifyEnabled true
*** proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
My question is about the progardFiles vs using multiDexKeepProguard. The documentation states:
File multiDexKeepProguard
Text file with additional ProGuard rules to be used to determine which
classes are compiled into the main dex file.
If set, rules from this file are used in combination with the default
rules used by the build system.
So if i do not use the multiDexKeepProguard then my classes still get compiled but may not end up in the main dex file, is that correct ? I am not clear how this differs from proguardFiles.
Android documentation also references this.
If you're enabling proguard in your application it's usually necessary to define proguard rules. proguardFiles are meant to be the instructions for progurard to minify or obfuscate your app.
multiDexKeepProguard is specifically for telling multidex which files are important to load at app startup and therefore what to keep in the main dex. As far as i'm aware, it just uses the proguard syntax as a convenience. This is optional and will usually only be set if there is an issue at runtime.
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.** { *; }
My main goal is to remove unused assets and libraries from the apk so that I can reduce it's size. I found Resource Shrinking while I was browsing through this SO post.
So I changed my gradle file to this:
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId 'com.galleri5.android'
multiDexEnabled true
minSdkVersion 16
targetSdkVersion 23
versionCode 12
versionName "0.10"
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
And to check whether this was working, I generated the apk and the size of the apk was same as before, no shrinking. So I tried to see if/which resources are being removed by running this command:
./gradlew clean assembleDebug --info | grep "Skipped unused resource"
And this is the output that I am getting:
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
I don't see and resources that are getting removed. Is there something I have done wrong or am I totally misunderstanding this tool? Any help would be highly appreciated. Thanks.
Edit I am just checking the size of the apk(apk-debug.apk) generated after running the app with above configuration. Does shrinking happen when I generate signed apk or would it be visible in the normal apk too?
Resource shrinking will only occur in your release builds, as that's what you've specified in your build.gradle. You could add the following closure to buildTypes if you want to minify in your debug builds, generally this isn't done.
buildTypes {
debug {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
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.
I have an Android library project and an app, which uses that library project. In the app's build.gradle file I have ProGuard obfuscate the whole app including the library project code:
//from the app project
buildTypes{
release {
runProguard true
proguardFile 'proguard-project.txt'
}
debug{
runProguard false
}
}
All works well, the lib project and the app project both get obfuscated just fine.
However, I want to build my lib project "standalone", so I can distribute it without the app project. This of course means obfuscating the lib project on its own.
I cannot do this by using the buildTypes code in the lib-projects build.gradle and simply running proGuard, since this would break the app-project build, because then building the app would compile its code agains already-obfuscated lib-project code... :-)
What I need is a possibility to run ProGuard only if I build the lib-project in "standalone" mode, e.g. by passing a parameter, or executing a special task.
Can anyone point me in the right direction?
solved it myself. In the lib-project's build file I have two buildtypes and I publish them both:
publishNonDefault true
and the build types:
buildTypes {
debug {
debuggable true
runProguard false
}
release {
runProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
}
this causes the debug and release APK to be created. In my app project's build file I then add an explicit dependency to the debug configuration by stating the dependency as follows:
dependencies{
compile project(path: ':FancyLibProject', configuration: 'debug')
//...
}
Of course in the app-project I also have two build types debug and release, which in turn invoke ProGuard in the same way as for the lib-project.
Note: I omitted the signing configs for the release build - without those the APK will cannot be installed!