When prepare to release the Android app, we would use Proguard to obfuscate the code which may break the app.
So once some error occurs, we modify that and build-release-test again and again, is there a simple way to do that?
Also, is it possible to see the obfuscated code directly in Android Stuido?
Retracing
Retracing is what makes the obfuscated stack trace human-readable by replacing obfuscated names to the ones used in original source. In order to be able to retrace, make sure to save mapping.txt for every build you are about to test or send to someone. Mapping is unique for every build process.
Mapping can be found in
appModule/build/outputs/mapping/release/mapping.txt
For retracing, there is a convenient tool named proguardgui, found in
$ANDROID_HOME/tools/proguard/bin/proguardgui.sh
(*.sh for UNIX systems, for other platforms expect different extension).
Choose "ReTrace", add a mapping.txt (make sure to use mapping.txt that was generated for that apk build) and paste the obfuscated stack trace into window. Press "ReTrace" on bottom right corner and you should get that stack trace with deobfuscated names.
Here's a screenshot with sample input I found on the Internet
Decompiling
Also, is it possible to see the obfuscated code directly in Android Stuido?
No, but you might see the code by converting dex to jar and decompiling it.
dex2jar myapp.apk
jd-gui myapp-dex2jar.jar
dex2jar github page.
jd-gui from github page.
Practices I use to follow
What works for me is whenever I add a library I add a proguard config for a library (usually provided by library developer).
There's also a project called android-proguard-snippets, thought they are mostly outdated, so make sure to check from library developer first.
For easy management, I use to split the proguard config for every library in a separate file. Below is a snippet from one of my build.gradle buildTypes for release
proguardFile getDefaultProguardFile('proguard-android.txt')
proguardFile 'proguard-dart.pro'
proguardFile 'proguard-parceler.pro'
proguardFile 'proguard-retrolambda.pro'
proguardFile 'proguard-rules.pro'
proguardFile 'proguard-rx-java.pro'
proguardFile 'proguard-support-design.pro'
proguardFile 'proguard-support-v7-appcompat.pro'
For objects used in Firebase serialization, add #Keep annotation.
For fields used by Gson, add #SerializedName annotations or use #Keep on an object.
Whenever I use reflection (or Animators, which are technically the same) I make sure to add proguard rule to keep the code accessed by reflection just after I write such code. In the end there will be probably nothing to fix.
Related
I have an empty project (no classes whatsoever, no activities), only a dependency to com.google.android.material:material:1.3.0 for the code to compile (there is a style defined which uses this).
I enabled shrinking option, yet, after generating the signes APK, there is a classes.dex file in the apk with a shitload of code, even though the app has no code. Why and how to I get rid of those, to make sure the apk contains only what is needed, no extra bloatware? Thank you.
This is the expanded apk:
For code shrinking please turn on Proguard and you have the option to customized Proguard rules as you need.
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
}
}
TRY 1
Open .jar file (appodeal.jar) and remove all .dex files.
TRY 2
Use the built-in inspection Java | Declaration redundancy | Unused declaration.
To run it on whole project go to Analyze -> Run inspection by name..., type Unused declaration and select desired scope. Then carefully check output and mark some classes as entry points if needed.
Select Unused declaration node in list and perform Safe delete action on all unused declarations at once.
I have some library modules in my application and there are a lot of resources that are unused. But they did not remove in release build.
minifyEnabled has been set true in release buildType and also shrinkResources has set true in app module.
Note: Proguard worked correctly and remove all of the unused source codes.(including library module source codes).
P.S: I know that we can't use shrinkResources option in library modules and this shrinking is app module responsibility.
UPDATE1 : I read this thread's answers. Although I didn't use getIdentifier(), it hasn't effect.
UPDATE2: According to below quote, I checked R class in output apk and I found references(id) to unused resources. Why are they there?
Proguard works on the Java side. Unfortunately, it doesn’t work on the resources side. As a consequence, if an image my_image in res/drawable is not used, Proguard only strips it’s reference in the R class but keeps the associated image in place.
source
I checked shrinkResources option and it works somehow.
shrinkResources did not remove unused resources rather it clears file contents. For unused xml I saw an empty xml like this in the output apk:
<?xml version="1.0" encoding="utf-8"?>
<x />
I don't know the reason behind clearing content instead of deleting the file.
This approach is not optimal but is acceptable. An empty xml like the above one occupies 47 bytes.
I want to optimize my app size.For that,I want to analyze my app with android studio analyze apk tool.But it only show the file size not details report.
Please see https://medium.com/#arungiri.10/managing-application-size-50810d03b16c for detailed info.
Use below lines to enable app shrinking using R8.
buildTypes {
...
release {
...
useProguard false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
...
}
}
where proguard-rules.pro must be written by you based on your app. Generally this file contains lines of code to not obfuscate data / model classes.
Once you do this, a APK will be generated in the output/release directory. Use that APK in APK analyzer tool.
As we can see the data present inside a APK below.
If you click on classes.dex, it would show the classes present inside this dex file.
As you can see, the code is proguarded, you will also get option to add respective mapping.txt file which would help in reverse engineering and see the APK package data.
mapping.txt file is auto-generated and is present under outputs/mapping/release/. Use this to reverse engineer and see the actual class names.
Also, to add proguard rules for data classes, it's easy. Just select the class or package you want to keep from proguarding, right click on it and select "Generate Proguard Keep rule" as shown below.
Proguard rules will be auto generated, copy this and put it in above proguard-rules.pro file.
Hope this answer helps.
I've been doing Android development for a little bit and I'm getting to a point in one of my projects where I would like to use Proguard to shrink the size of my APk and help with the dex limit. Unfortunately, I am getting a few errors and stack overflow has answers but they seem to be targeted for those with more experience.
My question is what is the relationship with your proguard-android.txt and proguard-rules.pro? Why are there two separate files and why are they in separate formats? When are the statements in these files called and in what order? I am just looking for an explanation of the overall context of using Progurad in a development environment.
Thank you in advance.
ProGuard manipulates Java bytecode the way you tell it with your configuration files and the rules they contain. ProGuard can do many things. And it can completely break your app so you have to make sure to add the correct rules.
I assume you use Gradle based builds for your apps. Then you've probably encountered this snippet that enables ProGuard for release builds of your app (or Android library):
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(‘proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}
In the config the list proguardFiles tells the build what files that contain ProGuard rules it has to use. This list can contain any number of files.
Why are the files (proguard-android.txt and proguard-rules.pro) defined differently?
The magical getDefaultProguardFile(‘proguard-android.txt') loads file named proguard-android.txt from the standard location in the Android SDK (the location is ${ANDROID_SDK}/tools/proguard/).
Other config files are resolved locally, so file proguard-rules.pro is expected to be at the root of the current Gradle module.
Why are there two separate files? And what is the relationship between proguard-android.txt and proguard-rules.pro?
ProGuard configuration is additive. You can define some rules in one file and other in other files. The rules are internally concatenated into single list of rules.
File getDefaultProguardFile(‘proguard-android.txt') contains several general rules for all Android apps (check them yourself, in the file in your SDK). The local proguard-rules.pro is expected to contain rules specific for your own app. For example you want to make sure that a class is not stripped away when you use it only through reflection (I'll get to that later).
Note that having multiple local files is very useful. For example you can use two local config files for debug builds - one with the release rules for your app and the second containing rules disabling obfuscation.
Also note that the additive behaviour of the configurations can be a bit troubling. If you add a rule in one config file, you cannot remove it in another. So be careful with very general rules (e.g. imagine adding -keep class ** { *; }).
When are the statements in these files called and in what order?
You can define them in any order, there's no difference. And you can define the same rule in multiple files, it doesn't matter. The order of the specified files doesn't matter either.
ProGuard itself is run as a single job within the Android build (single Gradle task to be precise). The task is provided all the inputs:
classes to manipulate
library classes to use but not manipulate
output path for generated processed jar
ProGuard rules specifying the manipulation
output paths for various output information (what was removed, mapping, …)
And then it processes the files and generates an output which is further processed by the Gradle build.
How does ProGuard actually work? And why do I need the rules?
ProGuard traverses the whole call graph of classes/methods/fields/…. It starts with the classes/methods/… defined by the provided rules. Then traverses the call graph and marks classes/methods/fields/… as necessary and keeps them for the output. So if you call it with no matching keep rules it will generate an empty output (or maybe it will throw an error and tell you to define some, I don't remember now). ProGuard doesn't recognize calls done via reflection, so you have to add some rules to handle that. There are many other cases that require you to add some rules, check the documentation for that.
Final notes
If you check ProGuard documentation you can find various rules
you can use. But not all of the rules are good for Android (ProGuard is a general Java tool).
Some rules are generated by Android build itself, you don't have to define them yourself. There are 2 types of such rules:
General config rules like -injars, -libraryjars, …
Rules generated from AndroidManifest.xml and resources (layouts). Android build (aapt tool) generates rules to keep classes mentioned in the manifest (activities, services, receivers, …) and custom views used in layouts. You can check these generated rules in build/intermediates/proguard-rules/${PRODUCT_FLAVOR}/${BUILD_TYPE}/aapt_rules.txt
Some rules can come from aar libraries. The libraries can contain ProGuard config necessary for the library to work (there can be proguard.txt file inside).
When writing Android libraries yourself be extremely careful with the rules you want to add to the aar. Because of the additive nature of the rules, it can cause problems for the app that bundles the library.
I am using Android Studio version v2.1.2 and Proguard doesn't work when I try to sign the release build, the build fails dramatically.
According to this link from developer docs Jack does obfuscation automatically.
Handles shrinking, obfuscation, repackaging and multidex Using a
separate package such as ProGuard is no longer necessary.
I had to disable minifyEnabled flag and remove the line where we load proguard file; to get it working, after doing this; I inspected the apk file generated by doing the above and I cannot tell whether Jack really obfuscated and reduced redundant code as the release apk size is same as the debug apk size.
I need to understand how to make obfuscation work with the newer compiler as the documentation doesn't really help.
I am looking forward to understand the following questions.
Does Jack work without Proguard file?
Is there a way to specify Proguard file?
The Jack compiler has its own Shrinker and Obfuscator that re-uses existing Proguard rules (see supported directives).
The configuration should be the same as before, so you need to add the following to your buildType configuration:
minifyEnabled true
proguardFile getDefaultProguardFile('proguard-android.txt')
proguardFile 'your-proguard-file.txt'