I want to use the facebook sdk just for analytics, is there an optimized proguard configuration that I could use to strip out the rest?
When it comes down to huge size, it is mostly about the resources rather than worrying about minimising java code itself. So you could actually try out a few things mentioned below.
Proguard works on the Java code. Unfortunately, it doesn’t work on the resources folder. 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.
Lint is a static code analyzer that helps you to detect all unused resources with a simple call to ./gradlew lint. It generates an HTML-report and gives you the exhaustive list of resources that look unused under the “UnusedResources: Unused resources” section. It is safe to remove these resources as long as you don’t access them through reflection in your code.
However Lint can tell you where are unused resources, but with fb sdk it will be hard to delete the resources as it comes from maven repository.
Minimising Resource Configuration (build.gradle)
For eg Fb sdk provides support all languages which you might not need, or all folders images like mdpi which may not be useful for you.
defaultConfig {
resConfigs "en", "de", "fr", "it"
resConfigs "nodpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
}
If all of this doesn't work that means either native code is bloating your apk, where application binary interface split might help reducing your apk size.
ABI Split:-
splits {
density {
enable true
reset()
include "ldpi", "mdpi"
}
abi {
// Enables building multiple APKs per ABI.
enable true
// By default all ABIs are included, so use reset() and include to specify that we only
// want APKs for x86, armeabi-v7a, and mips.
// Resets the list of ABIs that Gradle should create APKs for to none.
reset()
// Specifies a list of ABIs that Gradle should create APKs for.
include "x86", "armeabi-v7a", "mips"
// Specifies that we do not want to also generate a universal APK that includes all ABIs.
universalApk false
}
}
I think something could be done here as I opened facebook sdk gradle file ... it has few transitive dependencies, which is redundant and might conflict with your support version so you could either import the same in your files
dependencies {
// Facebook Dependencies
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:customtabs:25.3.1'}
It could be removed from final fat jar as you might be already using the support dependencies in your project that too different or conflicting ones .. so you could ideally exclude transitive dependencies based on your requirements something like below
compile ('com.facebook.android:facebook-android-sdk:4.+') {
exclude group: 'com.android.support' //by group
}
My question was wrong, there is no way to strip the code in proguard, probably it can be done playing with exclusions in gradle, but I do not think is doable using just proguard
In any case facebook finally has modularised their sdk, so for people like me that are insterested just on analytics, facebook-core is the only depedency we need to import
The ProGuard config included in the Facebook SDK has been updated and so it will correctly remove all classes that are not being used by your app (e.g. everything not in Analytics).
... except for anything Serializable which Facebook feels it is unsafe to remove ...
It now looks a lot like the older answers around here:
-keepclassmembers class * implements java.io.Serializable {
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
(as of 20 May 2016)
Which means that we don't need our own ProGuard settings for Facebook anymore. Gradle will automatically use the rules supplied by the SDK.
Of course, this still requires that your build.gradle is configured to run ProGuard:
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-release.pro'
-keep class com.facebook.** {
*;
}
-keepattributes Signature
I use these 2 lines and working everything fine
Related
I have created a Wear OS module in our existing Android mobile application. Both application modules remain in the project like following:
Project/app
Project/wear/wear_presentation
Project/otherLibModule/other_lib_module_presentation
and we have a common.gradle that is used by app and wear_presentation:
buildTypes {
release {
minifyEnabled true
debuggable false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled false
}
}
Before Wear OS module, we had a single proguard file inside app module that handles all minifying work, but now we have 2 application modules that have separate proguard files. That forces me to copy/paste all content from app proguard file into wear_presentation proguard file and this approach seems amateurish.
I want to use a single common proguard file that is used by both app and wear_presentation modules.
Is it possible?
Importing from Libraries is supported already.
https://developer.android.com/studio/build/shrink-code#configuration-files
/proguard.txt - If an AAR library is published with its own ProGuard rules file, and you include that AAR as a compile-time dependency, R8 automatically applies its rules when compiling your project.
Using rules files that are packaged with AAR libraries is useful if certain keep rules are required for the library to function properly—that is, the library developer has performed the troubleshooting steps for you.
However, you should be aware that, because ProGuard rules are additive, certain rules that an AAR library dependency includes cannot be removed and might impact the compilation of other parts of your app. For example, if a library includes a rule to disable code optimizations, that rule disables optimizations for your entire project.
/META-INF/proguard/ - for JAR libraries
It's commonly used by libraries like OkHttp https://github.com/square/okhttp/blob/okhttp_4.10.x/okhttp/src/main/resources/META-INF/proguard/okhttp3.pro
In a big project while former developers already worked on, you can find dependencies in gradle that doesn't have any usages at all.
Do those dependencies affect apk size? and how dependencies affect apk size, what if you're just using one method from a library, does this mean that all the library files attached to your apk.
Yes, unused dependencies do increase the apk size.
Enabling
minifyEnabled true
can analyze all the bytecode and remove unused classes and methods.
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
shrinkResources true
}
}
It is good to remove all the unused dependencies from gradle
Do those dependencies affect apk size? and how dependencies affect apk size
Yes of course.
The dependencies are added in the final apk, so the classes and the resources are added and they increase the size of the apk.
what if you're just using one method from a library, does this mean that all the library files attached to your apk.
Yes, all the library is attached.
There are some features in gradle to add a dependency removing the unused resources.
Yes gradle dependency definitely affects your apk size. if you are not using gradle dependency anywhere in the project then please remove the dependency
And even if you are using one mehtod from a library all files are attached to your apk. so to avoid this enable proguard tool with shrinkResource as true. This will obfuscate and simply remove unused method in the library and reduce the apk size
This might be too broad, but I would like an explanation on how Proguard and minification configurations are passed between projects and their dependencies to understand how deeply are these operations made in my project's dependency tree.
I have on build.gradle of `themodule':
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dependencies {
compile project(':someothermodule')
compile 'some.maven.central.library'
}
By the configuration it seems clear that the classes inside themodule will be minifyed and obfuscated, but what happens with the classes of someothermodule? Will they also be minifyed and obfuscated? Even if someothermodule has minifyEnabled true?
What happens if 'someothermodule' is just a .jar dependency?
What happens with the configurations of some.maven.central.library?
Are the Proguard configurations of the module being built cascading down to its dependencies or each of them follows its own rules?
If a module get obfuscated (minifyEnabled true) by itself, the used configuration is not automatically inherited by the consuming module (in your case the application).
There is a mechanism in the Android gradle plugin to enabled this:
consumerProguardFiles 'proguard-rules.pro'
The rules that are contained in proguard-rules.pro will be automatically included in the application project and merged with other configuration files.
This will only work for Android library projects (.aar). If you have a dependency to a .jar file on maven central for example, no such consumer rules will be available, and you have to add the needed configuration to your application project yourself.
Keep in mind that the configuration to obfuscate a module and the one used by the consuming application / module does not need to be identical. The consumer rules will in most cases only be a set of -keep rules.
Technically, it is the following :
Library projects by themselves don't run ProGuard, so they don't use
any configuration.
Application projects obfuscate the entire code base, including any
referenced libraries, so they need proper configuration for the
application code and for the library code.
I had a small case where I had a Facebook library as a gradle dependency and since we were obfuscating the code with minifyEnabled:true we had to keep all its code from being obfuscated, using the regular keep commmands such as :
-keep class com.facebook.** { *; }
Additionally, and regarding the .jar obfuscation, you can check this other post
Regards,
Since I am using many dependencies in my app, I am reaching the 65k Method Limit (I am reaching 76k methods). I've read on android.developer that proguard is used to shrink the code.
So - does proguard only shrink my application code or does it shrink the code of my dependencies too? Do I need to be wary of something when shrinking code with proguard? How do I do that?
My Gradle Build:
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "some.Path"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
configurations {
compile.exclude group: 'org.apache.xmlbeans'
}
repositories {
maven { url "https://jitpack.io" }
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.github.PhilJay:MPAndroidChart:v2.1.0'
compile 'com.opencsv:opencsv:3.4'
compile 'org.apache.poi:poi:3.12'
compile 'org.apache.poi:poi-ooxml:3.12'
}
TL; DR: invert your -keep option unless you love troubles
Firstly: I believe, that you are making right choice by using Proguard to overcome the dex limitation. I would not recommend using multidex support library under any circumstances: it introduces problem of multiple classloaders in your application, and that may backfire in many non-obvious ways.
Here is my personal approach to shrinking the app efficiently:
Pick a couple of hugest third-party dependencies you have;
Check if those really support Proguard;
If they do, shrink them with Proguard;
If you still don't fit in maximum method count, do steps above for some of remaining dependencies;
If you still don't fit, possibly reevaluate some, that do not support Proguard, possibly read their source code to get better idea why they don't, and apply Proguard to them yourself;
In the worst case, apply Proguard to your own code;
If absolutely nothing of above helps, use multidex.
Picking dependencies for shrinking
In your case, there aren't many (direct) dependencies in the first place. You may want to look at output of gradlew dependencies to get better idea of your indirect dependencies, some of which may be biggest contributors to total app size. Then you may proceed to use some of tools listed in "Dex" section of Android Arsenal to learn which libraries contribute most to dex method count. You seem to already have a general idea of it, so I won't dwell much on this part.
Remember: shrinking executable code is somewhat non-trivial intervention in library internals, so you'd rather shrink less to avoid mysterious problems in future. If in doubt, start from libraries, that openly declare, that they do support Proguard officially (in your case that would be Android Support libraries).
Note, that "supporting Proguard" may mean different things for different developers. You can expect Android Support Library developers to be at least basically competent, but many others will ship with consumer Proguard rules like this:
-keep class com.example.library.** { *; }
In case you wonder, the above config is based upon many real-life configs, such as Square's Leak Canary Proguard configuration. It does not say anything about overall competency of developers in question, just reminder that using Proguard can be hard. And yes, this kind of configuration will completely prevent shrinking and obfuscation of the library, unless you build it's local copy from source code and remove such helpful consumer-proguard-rules.pro from there.
Evaluating dependencies for Proguard
As shown above, even experienced developers sometimes choose to ignore Proguard. If Google searches regarding the library and it's compatibility with Proguard return nothing (and even if they do return some results!) you may have to make your own judgement regarding usage of Proguard. Here is how I personally do:
If there are words "framework", "enterprise", "reflection" anywhere on the library site, it is likely to be poorly compatible with Proguard;
If the library has anything to do with compile-time code generation (a-la Butterknife, Dagger etc.), think twice before using Proguard;
If the library messes with JNI, think a couple more times before using Proguard on it, and Google for it's effects on Proguard even if you don't shrink the library itself;
If in doubt, Google for it and/or read library source code: usage of Class.forName as well as Proxy.getInvocationHandler and similar reflection code are usual bad signs.
Libraries, that offer Android UI components (such as MPAndroidChart) are usually ok to shrink, at least if you keep getDefaultProguardFile('proguard-android.txt') in your Gradle config.
The most important part
A lot of developers (including Proguard developers themselves!) will offer you a misguided recommendation to start from empty Proguard config + default Android Proguard configuration, and eventually add -keep rules when necessary.
DO NOT DO THAT!!
Those advices come from people, who are either too badass to understand problem of average developer (read: "the Proguard developer himself") or don't have a clue about using Proguard properly. In fact, these kind of misguided practices are the very reason, why many answers to this question warn you against using Proguard: it's default behavior is like suggesting someone to start mountaineering from scaling the Everest.
Default Proguard configuration will obfuscate, shrink and optimize everything—your entire application with all dependencies except some classes you explicitly exclude. You don't want that, unless you have absolute understanding of every library and line of code in your projects: how they work and interact with each other, which techniques they internally use etc.
Instead you want to do the minimal necessary intervention (shrinking the code to reduce dex method count) in the minimal possible range (few hugest libraries) with minimal consequences (only where Proguard is known to work for sure). Here is my Proguard config for such cases:
-dontoptimize
-dontobfuscate
# Prints some helpful hints, always add this option
-verbose
-keepattributes SourceFile,LineNumberTable,Exceptions,InnerClasses,Signature,Deprecated,*Annotation*,EnclosingMethod
# add all known-to-be-safely-shrinkable classes to the beginning of line below
-keep class !com.android.support.**,!com.google.android.**,** { *; }
Add the above rules to your app's proguard-rules.pro, they will shrink only classes, that you explicitly allow to shrink. Append wildcards for other safely shrinkable packages (exactly as above—with ! and .** parts) to beginning of the -keep line.
As an alternative to ProGuard you could use the built-in Gradle shrinker by turning off ProGuard but still reference a ProGuard config. This will remove unused code but not obfuscate or do any other "magic". Although recommended only for Debug builds I don't see why you can't use it for Release builds as well if you don't think that you need obfuscation.
The main benefit, compared to ProGuard (in my opinion) is that you avoid tight coupling between a ProGuard configuration and the structure of your codebase and third party dependencies.
build.gradle:
minifyEnabled true
useProguard false
proguardFiles ('proguard-basic.pro', getDefaultProguardFile('proguard-android.txt'))
proguard-basic.pro:
-dontwarn javax.**
-keep class com.mycompany.** { *; }
If you enable minification via ProGuard, it will also minify your dependencies.
Libraries are typically not already obfuscated/minified with ProGuard. Some libraries will not work properly by default if they are obfuscated, so you should check any libraries you use to see if they have any documentation surrounding ProGuard. Butterknife, for example, has a few special ProGuard rules that you need to include to ensure that it continues working properly.
For me you should rather look for multidex, to go beyond 65k limit, not proguard as in longer run the later is not a solution to your problems.
See docs: https://developer.android.com/tools/building/multidex.html
If you enable minification in your build.grade file, then yes it will also shrink your dependencies.
Keep in mind that Proguard may introduce unwanted side effects. Not all libraries/dependencies can be shrunk as Proguard also obfuscates the code. (i.e. turns String name into String n) and removes unused code.
Take a look at this Github project: https://github.com/krschultz/android-proguard-snippets
As an alternative, you can look into using MultiDex. You can read about it here: https://developer.android.com/tools/building/multidex.html
As per the new Android Studio update in version 3.2 new code shrinker that also obfuscates by adding the line below to your project’s gradle.properties file
Add This line:
android.enableR8 = true
I'm using AppCompat support library in my Android project. AppCompat has plenty of drawables and resources which I don't use in my app. That unnecessary files increases my 900K app to above 2M, which I don't like.
Is there any way to exclude those files when creating the APK file? Or I should obfuscate the library in my code instead of making a dependency?
I'm using Gradle in Android Studio.
Thanks
EDIT 1 I am using proguard already. but proguard can't know I don't want to have drawable-xxxhdpi or values-it for example.
EDIT 2 I am also using Android Lint, which can't help me, beacuse I don't access to lib's code directly, and android adds them when building the APK file.
Starting from version 24.2.0, the v4 Support Library has been split into several smaller modules.
So, apart from using shrinkResources and proguardFiles, also make sure that you are using only the specific modules that your app needs. e.g.
If your app only uses Compat utils like NotificationCompat, ContextCompat or ResourcesCompat etc., use only the compat module as:
compile 'com.android.support:support-compat:24.2.0'
From Android Gradle Build System, since version 0.7.0:
New option on product Flavor (and defaultConfig) allow filtering of resources through the -c option of aapt
You can pass single value (resConfig) or multiple values (resConfigs) through the DSL.
All values from the default config and flavors get combined and passed to aapt.
See "basic" sample.
In the "basic" sample:
defaultConfig {
...
resConfig "en"
resConfigs "nodpi", "hdpi"
}
So, try the following to achieve what you asked for:
productFlavors {
...
frOnly {
resConfig "fr"
}
...
}
Note that you might also want to include *dpi, port, land, etc.. as well.
Answer is from: Android Studio exports strings from support library to APK, thanks to Primoz990
shrinkResources can also be an option for you. It is available since 0.14 - but be careful - it still has some pits like protect resources when using shrinkResources
Although OP has cleared up that he is using proguard, I would like to post some code if it helps someone because I am able to shrink my app from 3.8 MB to 3.1 MB using the accepted answer and further to mere 1.8 MB through proguard. I used this configuration in my app level build.gradle file:
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}