Android Gradle build resulting apk contains both obfuscated and non-obfuscated classes - android

When building my android project, I have added the following to the build.gradle file to enable proguard:
buildTypes {
release {
runProguard true
proguardFile 'proguard-project.txt'
proguardFile '../common/proguard-shared.txt'
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
Everything builds okay BUT when I disassemble the resulting dex file, it turns out that both the obfuscated and non-obfuscated files are there.
For example, both common.Base64 and common.a exist, the first is non-obfuscated, while the second is.
Not sure its related, but the project itself has a non-typical structure.
This is a result of us having a large android code base with more than 40 android apps.
We are trying to create a gradle based build flow side-by-side of existing eclipse based build.
If all goes well, we intend to change the file structure to be more native gradle, and start using flavours and build-types to have-away with many of the libraries we created to accommodate for the lack of flavours and such.
Project E above relies on a chain of libraries like that:
E -> D -> C -> B -> A
e.g. The E project depends on the library D which depends on library C ... all the way up to A.

After looking into this, I found out that this is a problem if you first build without proguard enabled and then build it with it enabled. This is due to the incremental mode of dex.
You can do a clean build after enabling proguard and it'll fix this.
Edit: I previously indicated that you can disable incremental mode in dex, but it turns out that actually doesn't help!

Related

Configure proguard in a multimodule SDK Android library project

I'm working on an Android SDK that is about to be split into separate modules, with some code being shared between others, namely:
shared classes
SDK with feature #1
SDK with feature #2
There is a need of obfuscating the output AARs. With one single module it's not an issue, but I can't find how to configure the whole project correctly with Proguard.
How does the Proguard obfuscation work in case when I want to publish a new version of all these libraries? Will all the modules be obfuscated separately? How can I make sure that all the modules will be obfuscated at once and the release version of the libraries will correctly refer to all the artifacts that are located in the shared module?
Not sure I understand the issue.
But if you want to release 2 SDKs now instead of 1 (before split), I don't think the setup should differ very much.
Just make sure you have proguard file in these SDKs and you are using it in the build script specific to that SDK (module). Then just run something like :sdk:assembleRelease and you are done.
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'library.pro'
}
It is described in detail in this issue:
Obfuscating the .aar files
I understand that shared module is not published, so there you just need to define specific proguard rules that will be used by other modules, like this:
release {
minifyEnabled true
consumerProguardFiles 'onboarding-proguard-rules.pro'
}

Why Proguard runs despite having minifyEnabled in gradle config?

According to documentation setting minifyEnabled to false must disable ProGuard run
integration {
minifyEnabled false
versionNameSuffix "-int"}
But the ProGuard is still started by Gradle! Any ideas why?
You will need to change the Build Variant to use one of the integration build variants (from the bottom left in android studio), if you want to use the configuration for integration buildType.
From what you describe you appear to be using a different buildType. By default it is debug. Make sure an integration Build Variant is selected and you should be good.
As it often happens, I found and answer after posting my question.
"This is not a bug, this is a feature":
https://groups.google.com/forum/m/#!topic/adt-dev/iS_lyRH8hL8
This is not really a problem, but certainly annoying.
The output you are seeing is related to the way the Android gradle plugin determines the set of classes that must be in the main dex file when multidex is enabled. For this purpose it uses ProGuard internally, but it is unrelated to your configuration.
In order to disable the logging output of this task, you can add the following to your build.gradle file:
tasks.whenTaskAdded { task ->
if (task.name.startsWith("transformClassesWithMultidexlistFor")) {
task.logging.level = LogLevel.ERROR
}
}

Application too big? Unable to execute dex: Cannot merge new index into a non-jumbo instruction

I am getting the following error when I compile my app:
[2014-05-07 21:48:42 - Dex Loader] Unable to execute dex: Cannot merge new index 65536 into a non-jumbo instruction!
I am at the point that if I declare a new method anywhere in my package, I get this error. If I don't, the app compiles.
I would like to know what exactly (and accurately) does this error mean. My app is big, but I don't think its that big! So:
Does the error mean I have too many methods? public? static? package? members?
Is it related to the methods/members of my root package, or also to the included JAR libraries?
Is there a way to get more debug information about this?
I already know about that "jumbo" enabling flag addressed in the similar questions here in SO, however, I think jumbo mode is not available on the API level I'm targeting (ICS).
Your error is for the amount of strings (methods, members, etc) in a single dex file.
You need to compile you app using jumbo in dex with:
dex.force.jumbo=true
in project.properties
This increment the limit for strings in a dex files. And your project will probably compile.
Also with jumbo set, the is another limit of 64K only for methods in an single dex. If you get this limit in the future , you will need to remove some dependencies.
UPDATE: for build with Gradle:
In Gradle you can enable jumboMode also in the build.gradle file with:
dexOptions {
jumboMode = true
}
Check:
Android Build: Dex Jumbo Mode in Gradle
Also with Gradle you can avoid the 64K limit for methods using multidex build, tutorial here:
https://developer.android.com/tools/building/multidex.html
For gradle build, just add the dexOptions into build.gradle to enable jumbo mode:
android {
dexOptions {
jumboMode = true
}
}
Remember to run "gradle clean" before your new building.
It's related to the number of methods of libraries included in the project. For example if you have tracking in your app, just Google Analytics is ~7000 methods.
In one of my projects using Lombok (2MB of JAR) gave me these problem. Solved getting rid of this library.
It looks like the problem occurs because all the class files from your project and JAR files are packed together before DEXing. This may not be completely true but any way of controlling this in our project has proven to be quite difficult. Even removing stuff that initially caused this problem, cleaning and rebuilding didn't fix the issue for us in a consistent way.
So we took this opportunity to switch our project to Android Studio and managed to solve the problem by turning on ProGuard for debug builds as well. More precisely we only use the shrink phase of the ProGuard's processing chain.
Gradle makes it very easy to turn on ProGuard for debug builds:
buildTypes {
debug {
runProguard true
proguardFile 'proguard-project-debug.txt'
}
}
And here is the debug ProGuard config we use:
-keep class com.your.code.**
# Use -keep to explicitly keep any other classes shrinking would remove
-dontoptimize
-dontobfuscate
-ignorewarnings
This does increase the build time of the project but the good side is that the debugger still works.
The only faster alternative I can think of is that any JAR files are manually stripped of the unused class files. But this is not only difficult to do it is also inconvenient when you want to use a slightly larger part of a library at a later time.
I hope this helps other developers struggling with this issue. And perhaps in the future Google can improve the compiler that does this pruning by default. Our APK DEX file went from 8MB to 2.9MB.
Newer gradle (1.0.0+) versions
In newer Versions of Android studio (1.0+) the bundled Gradle got updated. There were some changes on how the build mechanism works so your project Gradle file can now take advantage of the minifyEnabled and shrinkResources parameters. Current version is 1.1.0.
Keeping up with changes on a fast moving platform like Android takes effort but it is often rewarded with new features, tools and faster build times. So updating Android Studio and (carefully) updating your projects is worth the time you invest.
buildTypes {
debug {
proguardFile 'proguard-project-debug.txt'
minifyEnabled true
shrinkResources true
}
}
Some interesting observations. Same error may appear if you have multi-flavor project. It's confusing. Turned out that I attempted run app with generic command: gradlew installDebug. When I've changed command line to look like this problem is gone. Don't forget to replace Flavor part with your actual one.
gradlew installFlavorDebug

How to configure Proguard using Gradle?

I recently switched to Android Studio / Gradle and I am wondering, how ProGuard can be configured in the build.gradle script. I am new to Gradle, but I thought, configuring the Proguard task would be a good idea (as documented in the Proguard project documentation.
I want to configure Proguard to save the mapping in different files for different product flavors with the 'printmapping' setting
task myProguardTask(type: proguard.gradle.ProGuardTask) {
printmapping file("test.txt")
}
but it crashes on task-execution with
Gradle: Execution failed for task ':module:proguardFlavorVariant'.
> proguard.ConfigurationParser.<init>(Ljava/io/File;Ljava/util/Properties;)V
In the newer versions of the Gradle 'android'-plugin, Proguard seems to be included and I think this might be the reason, why configuring the Proguard task as stated on the Proguard documentation did not work. But I did not find any documentation on this topic of how to do this with the newer android-gradle-plugin.
Thanks for your help!
Proguard is built into the Android-Gradle plugin and you don't need to configure it as a separate task. The docs are at:
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Running-ProGuard
Are your flavors so different that you really want different ProGuard configurations for them? I'd think in most cases you could have one config that could cover them all.
EDIT:
If you do want to change ProGuard rules for different flavors, the Android Gradle DSL allows you to do so. The sample in the docs shows how to do it:
android {
buildTypes {
release {
// in later versions of the Gradle plugin runProguard -> minifyEnabled
minifyEnabled true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
productFlavors {
flavor1 {
}
flavor2 {
proguardFile 'some-other-rules.txt'
}
}
}
That should handle your use case, unless you're looking for a way to have it automatically determine the proguardFile value based on the flavor name without you having to set it manually; you could do that through some custom Groovy scripting.

Is it possible to use proguard in debug mode?

In my android app, i want to test some features with proguard on.
I don't need to really "debug" it, but i want proguard to run when i hit run in eclipse. I don't want to export the binary every time (so, in release mode) and save as apk and get it to the device to test.
Is there any way to run proguard in this way?
Update:
It seems like this is possible if you are not using Eclipse; as question title does not include Eclipse, there are multiple correct answers to this question.
If you want to make the whole build process easier for you, you should switch over to gradle and Android Studio IDE.
Then you could easily add the following to your build.gradle file to run ProGuard:
android {
buildTypes {
release {
}
debug {
minifyEnabled true
proguardFile 'proguard-android.txt'
zipAlignEnabled true
}
}
}
This will run ProGuard on your debug build, configured with the file "proguard-android.txt", which should be put at your project's root folder. And in addition your apk is being zip aligned (Just remove "zipAlignEnabled true", if you don't want that to happen). If you want to do the same for your release build, just add those three lines under "release".
Slightly off-topic: Stuff like adding dependencies, signing your apk or adding other custom tasks to your build process is also way more uncomplicated with gradle. In addition you'll be able to not only build your apk via Android Studio IDE, but also via a simple command on the command line (e.g. ./gradlew assembleDebug). So if you are working on a team, the setup process for new members is just one "./gradlew assembleDebug" away. Without the need for any IDE configuration at all. Importing your project including all dependencies is as simple as a one-click process
EDIT:
As of Gradle Android Build Tools version 0.14.0 the property names have changed (http://tools.android.com/tech-docs/new-build-system):
BuildType.runProguard -> minifyEnabled
BuildType.zipAlign -> zipAlignEnabled
I've updated the above code.
Old Answer :
http://developer.android.com/tools/help/proguard.html
ProGuard runs only when you build your application in release mode, so you do not have to deal with obfuscated code when you build your application in debug mode.
When you build your application in release mode, either by running ant release or by using the Export Wizard in Eclipse, the build system automatically checks to see if the proguard.config property is set. If it is, ProGuard automatically processes the application's bytecode before packaging everything into an .apk file. Building in debug mode does not invoke ProGuard, because it makes debugging more cumbersome.
Update: 13-3-2016
It is possible with the new gradle build system. You need to set minifyEnabled to true in your build.gradle file. Generally you have pro-guard running in release mode. There are other options available like shrinking resources. You can find some useful info # http://tools.android.com/tech-docs/new-build-system
Also do have a look #
http://developer.android.com/tools/building/configuring-gradle.html
android {
...
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
Regarding custom Ant builds (and based on Victor's answer), adding the following to my build.xml file works for me:
<target name="-debug-obfuscation-check">
<!-- enable proguard even in debug mode -->
<property name="proguard.enabled" value="true"/>
<!-- Secondary dx input (jar files) is empty since all the jar files will be in the obfuscated jar -->
<path id="out.dex.jar.input.ref" />
</target>
Notice that I had to override (actually pre-set) the out.dex.jar.input.ref; otherwise, the later running of dx will attempt to merge non-disjoint jars and throw the DexException: Multiple dex files define Xxx.
It is possible if you build with Ant. See Android custom build using Ant on how to build your project with ant. Then, simply override in the project's build.xml the target "-debug-obfuscation-check" and set proguard.enabled to true:
<target name="-debug-obfuscation-check">
<!-- proguard is never enabled in debug mode -->
<property name="proguard.enabled" value="true"/>
</target>
With Android Studio you can use -dontobfuscate option in your Proguard rules file and debugger will work fine. I'm not sure if it works with Eclipe as well.
If you are using AGP (Android Gradle Plugin) 7.2.0 or newer, beware that we have a bug open without a solution so far. Workaround as of now is to downgrade AGP to 7.1.3 so you can obfuscate your debug APK.
https://issuetracker.google.com/issues/242214899?pli=1

Categories

Resources