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.
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.
In my app's example, the debug apk is 20Mbs (from 13Mbs, after upgrading the Gradle version and Gradle Plugin version), and the release apk is just 5Mb. Why is that?
In the build.gradle file, the buildTypes part has remained unchanged, so there hasn't been any optimizations / shrinking from the R8 compiler or from ProGuard.
android {
...
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
RELEASE:
DEBUG:
You can see the big difference in size in the lib folder, among others.
There are more optimizations happen during the release builds besides ProGuard and R8. According to the documentation https://developer.android.com/studio/build/shrink-code.html
When you build you project using Android Gradle plugin 3.4.0 or higher, the plugin no longer uses ProGuard to perform compile-time code optimization. Instead, the plugin works with the R8 compiler to handle the following compile-time tasks:
Code shrinking (or tree-shaking): detects and safely removes unused classes, fields, methods, and attributes from your app and its library dependencies (making it a valuable tool for working around the 64k reference limit). For example, if you use only a few APIs of a library dependency, shrinking can identify library code that your app is not using and remove only that code from your app. To learn more, go to the section about how to shrink your code.
Resource shrinking: removes unused resources from your packaged app, including unused resources in your app’s library dependencies. It works in conjunction with code shrinking such that once unused code has been removed, any resources no longer referenced can be safely removed as well. To learn more, go to the section about how to shrink your resources.
Obfuscation: shortens the name of classes and members, which results in reduced DEX file sizes. To learn more, go to the section about how to obfuscate your code.
Optimization: inspects and rewrites your code to further reduce the size of your app’s DEX files. For example, if R8 detects that the else {} branch for a given if/else statement is never taken, R8 removes the code for the else {} branch. To learn more, go to the section about code optimization.
As i need to reduce the size of APK file, I have followed Apk Expansion guide to divide APK in chunks.
The Downloader library defines ways to download the expansion file, but i need to know the way to exclude resource files and aar files from the apk.
I found following, but these are neither removing any resource-drawable files nor any arr files, and the size of apk remains same.
For testing purpose, i have added drawables of around 4 MB and couple of arr files of size 3 MB. I am creating apk from Build->Build APK option. I don't know if following will effect only on signed APK.
sourceSets {
main {
resources {
exclude '**/drawable/*'
}
}
}
android {
packagingOptions {
exclude 'lib/armeabi/a.so'
}
}
If there are specific resources you wish to keep or discard, create an XML file in your project with a <resources> tag and specify each resource to keep in the tools:keep attribute and each resource to discard in the tools:discard attribute. Both attributes accept a comma-separated list of resource names.
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="#layout/l_used*_c,#layout/l_used_a,#layout/l_used_b*"
tools:discard="#layout/unused2" />
Save this file in your project resources, for example, at res/drawable/keep.xml. The build does not package this file into your APK. This way you can customize which resources to keep.
Expansion files may not contain any executable code. This is partly a Google Play policy, but also for security. Because they are written to a directory accessible to both your app and Play, and possibly to an SD card, if you put code their it would open your app to security exploits.
Because of this, you don't want to put AAR files in expansion files, as these normally have code. And many resources might not be appropriate, as these get compiled with your app and so have resource ids etc. Instead you should split out large elements that are not part of the explicit compile. Good candidates are things like:
Open GL textures
large sound files for sound effects
large level data or maps for games
large images
All of these could potentially be in the assets directory of your app and are prime candidates for expansion files.
If you have none of the above, if you are going over 100Mb in size it is likely that you are not Proguarding your code correctly, and including a lot of code your app doesn't use. If this is the case, then learning to use Proguard correctly is probably a bigger improvement than switching to expansion files. SO users may be able to advise you more if you can say where the size in your APK is going? How much on images? How much on executable code? Are you using Android Studio and java, native code, or a technology like Unity? All of these have slightly different approaches to APK size minimization.
There are 2 things that you can do.
Firstly, you can use Lint. Lint will help to highlight and remove all the resources that you are not using in your code including the drawables.
Second you can use Proguard. Using Proguard you can choose which version of APK you want to shrink including the debug(or main, as in your example) version. Just insert the following code.
android {
buildTypes {
debug {
minifyEnabled true
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
} }
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.
I've got problem with very simple application. APK size is now 3 MB, but it contains a lot of useless for me files (I think that source of this files is Support Library). In my application I don't use any images, but all drawable directories contains a lot of icons, buttons, etc. Is it possible to delete this images by any rule in gradle or other method? I use Android Studio.
Already I added to build.gradle information about languages to include in APK. I had in Hello World 80 languages before it.
Screen of files:
The Gradle build system for Android supports "resource shrinking": the automatic removal of resources that are unused, at build time, in the packaged app. In addition to removing resources in your project that are not actually needed at runtime, this also removes resources from libraries you are depending on if they are not actually needed by your application.
To enable this add the line shrinkResources true in your gradle file.
android {
...
buildTypes {
release {
shrinkResources true
}
}
}
Check the official documentation here,
http://tools.android.com/tech-docs/new-build-system/resource-shrinking