I created two aar-libraries: com.example:test1:1.0.0 and com.example:test2:1.0.0. I added these libs to my main app and after build Android-Studio throws error:
Duplicate class com.example.utils.a found in modules classes.jar (com.example:test1:1.0.0) and classes.jar (com.example:test2:1.0.0)
I find out that this error happens because both libraries have classes in same package (com.example.utils) and after obfuscation R8 creates classes (a.class) with same full name (I saw this in classes.jar of aar). If I disable R8 in properties of my libs this error has gone.
'a' is not library class: after obfuscation all library classe names remain unchanged and a.class was added in package additionaly by R8.
I read R8 documentation and found nothing about this problem.
Are there any ways to solve this issue without ranaming the package in one of my libs?
When creating two libraries it is best practice to use two different namespaces, as otherwise there will be the possibility of duplicate classes even without using R8 when "accidentally" a class with the same name is added to both. So in your case use com.example.test1 and com.example.test2.
Depending on you use case, it might also be a better option to not apply R8 to the libraries, but only apply R8 to the final app including the two libraries. Shrinking libraries are mainly to make distribution size smaller, and rename internals to avoid library users (accidentally or knowingly) depend on internals which might change between library versions.
When shrinking libraries you also want to consider the option -keeppackagenames to make sure that all renamed classes stay within the package of the library. Otherwise you might end up with class e.g. a.a.a.a.class in multiple libraries.
If this issue happens for libraries that you don't have control over tools like shadow can be used to relocate.
You should always prefix all of your code in Java or other JVM languages with unique packages to create a unique fully qualified name because any two classes with the exact same fully qualified name will cause a build error. This happens because the JVM only uses the fully qualified name string, saved initially in a single table, to instantiate all the classes and interfaces in the system. If the table will have more than one entry for a class/interface, it won't know which one to choose. You can read more about it here.
As I describe in my answer here, the best solution for obfuscation related collisions is to use -repackageclasses com.example:test#.ofs in the proguard-rules file of each library while replaceing # with 1 and 2 respectivly. This will move all the obfuscated classes into the com.example:test#.ofs package while all the non-obfuscated classes will remain in their original packages and you're guaranteed to have no collisions.
As the Proguard documentation states:
-repackageclasses [package_name]
Specifies to repackage all class files that are renamed, by moving them into the single given package.
Related
I'm writing 2 Android libraries. When I obfuscate both, the obfuscated code in both of them contains a class named a.a.a.a.a which causes the following error when trying to use both libraries in the same application:
Duplicate class a.a.a.a.a found in modules classes.jar (lib1) and classes.jar (lib2)
How can I prevent Proguard from obfuscating the first 3 packages to end up with:
my.domain.lib1.a.a and my.domain.lib2.a.a?
Edit: The obfuscation is happening as part of building the libraries, not while building the application.
This can be resolved by putting -repackageclasses my.domain.lib#.ofs in the proguard-rules file of each library while replaceing # with 1 and 2 respectivly. This will move all the obfuscated classes into the my.domain.lib#.ofs package while all the non-obfuscated classes will remain in their original packages and you're guaranteed to have no collisions.
As the Proguard documentation states:
-repackageclasses [package_name]
Specifies to repackage all class files that are renamed, by moving them into the single given package.
Another solution is to use -keeppackagenames. Unfortunately, I couldn't find a way to make it keep only the first 3 packages.
See the Proguard documentation:
-keeppackagenames [package_filter]
Specifies not to obfuscate the given package names.
Add the code below to the proguard-rules.pro file.
-keeppackagenames
I am trying to improve my understanding about usage of ProGuard for Android build. Am not very sure what to keep ( not obfuscate ) exactly and what to obfuscate.
Couple of libraries are being used in my App, some of them are listed below
com.android.support:appcompat
com.android.support:design
io.reactivex:rxandroid
io.reactivex:rxjava
com.jakewharton.rxbinding
com.trello:rxlifecycle
Question 1:
Should I obfuscate above libraries ? they are already open source.. will it not be wise to keep all support libraries using proguard rule:
keep class android.support.** { *; }
Similar way I can do with io.reactivex and jakewharton libraries
Question 2
Other than support and external libraries, I have application specific classes, coded for this application. Is it ok to keep the class names which are mentioned in AndroidManifest.xml and specifically their public members, and let obfuscate rest of the code.
Let me know if I am missing something or my understanding is not correct. just to repeat this is not a question on how to do it technically, but more on what to include and what to exclude for obfuscation/optimization/ shrink.. and more specifically reason behind it ....Definitely it will be espresso tested after build.
Question 1: Should I obfuscate above libraries?
Yes. If you add -keep <library> rules then the entire library will be included in your APK, which bloats it and might cause problems such as making your app exceed the 64k method limit and require Multidex. It's always a good idea to apply the ProGuard rules provided by the library. You can usually find .pro files in the libraries source code or in websites such as https://github.com/krschultz/android-proguard-snippets.
Question 2: Is it ok to keep the class names which are mentioned in AndroidManifest.xml and specifically their public members, and let obfuscate rest of the code.
The Android plugin already does that for you. The plugin scans all classes whose names need to be preserved (e.g. Activities, Services, BroadcastReceivers, Views, etc) and doesn't obfuscate them. This includes everything that you declare in AndroidManifest.xml.
I have enabled minifyEnabled in my gradle file for using the feature of ProGuard to obfuscates code as a result it throwing some error.
Note: there were 1 class casts of dynamically created class instances.
You might consider explicitly keeping the mentioned classes and/or
their implementations (using '-keep').
Warning: there were 309 unresolved references to classes or interfaces.
You may need to add missing library jars or update their versions.
If your code works fine without the missing classes, you can suppress
the warnings with '-dontwarn' options.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass)
and much more.
I have gone through the ProGuard documentation.i guess this is what happening wrong.am using third party libraries and jars.and it confuses as i have more number of libraries and jars.how can i decide which one should keep in my proguard-rules.pro and which one doesn't ? And what else need to do for using ProGuard successfully and efficiently ?
ProGuard analyzes all the code in your application (that includes libraries) and generates warnings if it sees unresolved references (or other problems, but unresolved references is the important thing here). It is very common for libraries to refer to system or library code that doesn't exist in your project.
For example: Picasso optionally integrates with OkHttp, and has code referring to that library. But if you haven't included OkHttp ProGuard will warn about unresolved references.
A successful build must not have any warnings from ProGuard. ProGuard also checks for reflection, if there are unresolved references found in reflection, ProGuard will generate notes instead of warnings. Both warnings and notes may point to issues with the classpath. You could check the code yourself to see if there's an actual problem, or trust the libraries that their code works on Android (and on your target platform version).
Kevin Schultz opened up a public collection of ProGuard configuration files. His blogpost should help you get started with your configuration file.
http://www.kevinrschultz.com/blog/2014/02/15/proguard-with-gradle/
Do double check the configurations that you copy. They can be a little aggressive sometimes. If possible you want to avoid -dontwarn/dontnote {entire-package} and instead narrow down on the actual problems.
You need to add proguard rules specific to the libraries you use. A compilation of such rules is available at https://github.com/StarWar/android-proguard-snippets/tree/master/libraries
You can also try adding dontwarn directive for packages causing issues. An example of this would be -dontwarn org.codehaus.jackson.**
Would any of you be so kind as to rephrase (in your own words) the explanations for some of the proguard keywords that are written in their manual? I have hard time understanding in full what some of them mean, and what changes if they are not there in the .cfg file.
The keywords I'm interested are:
1) -dontskipnonpubliclibraryclasses and -dontskipnonpubliclibraryclassmembers
The second is being explained as:
Specifies not to ignore package visible library class members (fields and methods). By default, ProGuard skips these class members while parsing library classes, as program classes will generally not refer to them. Sometimes however, program classes reside in the same packages as library classes, and they do refer to their package visible class members. In those cases, it can be useful to actually read the class members, in order to make sure the processed code remains consistent.
First of all, does it refere only in the context of external jars? Second, what is the difference between those flags reside in the .cfg file vs not being there?
2) -libraryjars, I'm lost for that one. What is the purpose of this keyword? On proguard manual page it reads:
Specifies the library jars (or aars, wars, ears, zips, apks, or directories) of the application to be processed.
So does it mean, that if I don't use this flag, then those jars are not put under the whole obfuscation process? But if that's the case, then why when I don't use this keyword, there are a lot of warnings for classes in those jars in the proguard output?
Next it says:
The files in these jars will not be included in the output jars.
What does it mean exactly? It means, that if this flag is set, then all other files aside .class files will not be included in the parent's application jar?
After hours of reading I think I got my answers. Especialy what helped me was reading many of the creator of ProGuard answers here at StackOverflow.
Let me start with the jars topic. Libraryjars is usually the platfrom jar, the application is build against, so android.jar is a good example here. This jar will not be processed, it's classes will not reside in the output apk, because they will be all on the clients device. They will not be obfuscated or shrunk, because a) even if they were they would not be coppied into the output apk anyway, and b) if they were obfuscated then it would crash application due to the fact that say Activity during obfuscation would have the name changed to "a", but on the clients device the android API is not changed.
So libraryjars is used for all the jars that proguard needs when processing our app, but which jars will not be included (or it's class files) in the final apk.
Injars on the other hand are all the jars that we want to be shrunk/obfuscated etc (unless we use keep* keywords).
Now the reason that I had so much difficulty was because there were conflicting information about those keywords all over the place. Some people said to use -injars, some said to use -libraryjars, some said neighter. What I found out later on, is that the last answer is correct. No -libraryjars or -injars keyword is needed because ADT does all this for the developer, and it uses the -injars keyword with all the jars residing in the /libs folder.
That is also the reason why I found many people using the "keep" keywords with the packages of one of the jars to ignore it's obfuscation/shrinking. The reason for it is that because ADT uses -injars keyword for those jars by default (and not libraryjars which would essentialy do the same in this context) then those jars are marked to be processed (obfuscated/shrunk). To negate this effect, people use -keep keywords for the packages of those jars.
As for the #1 question:
First of all, does it refere only in the context of external jars? The answer is no. It reffers to all the libraries even referenced inside the attached jars
Second, what is the difference between those flags reside in the .cfg file vs not being there? From what I found out it's for helping the ProGuard with processing of those libraries.
History/Context
I have a project[1] where size really matters - recently I moved stuff to a shared lib[2] and thought proguard will take care and remove the unused classes because I had a config that was drastically reducing the size but by using the lib i came over the magic 100kb mark so I investigated: classes which I do not use for sure are in the resulting dex file - and even with full name ( not shortened to single-char ) - e.g. I see the SquareView in the dex which I in no way use in the App.
Question
Surprisingly I found in the proguard documentation the following:
The library jars themselves always remain unchanged.
Can I somehow tell/trick proguard (in)to process them? I find this really strange especially because I expect more stuff to be removeable in the lib than in the App itself..
[1] https://github.com/ligi/FAST
[2] https://github.com/ligi/AndroidHelper
The Eclipse/Ant/Gradle build processes in the Android SDK automatically specify your code (from bin/classes) and its libraries (from libs) with the option -injars. This means that the complete application is compacted, optimized, and obfuscated (in release builds, assuming ProGuard is enabled).
The build processes only specify the Android runtime android.jar with the option -libraryjars. It is necessary to process the code, but it should not end up in the processed apk, since it is already present on the device.
So it should all work out automatically. You may still see entire libraries with their original names in processed apks, if your configuration proguard-project.txt contains lines like -keep class org.mylibrary.** { *; }. Such configuration is typically a conservative solution to account for reflection. With some research and experimentation, you can often refine the configuration and get better results. You can figure out why classes are being kept with the option -whyareyoukeeping.
I believe you have to use -injars:
-injars class_path
Specifies the input jars (or wars, ears, zips, or directories) of the application to be processed. The class files in these jars will be
processed and written to the output jars. By default, any non-class
files will be copied without changes. Please be aware of any temporary
files (e.g. created by IDEs), especially if you are reading your input
files straight from directories. The entries in the class path can be
filtered, as explained in the filters section. For better readability,
class path entries can be specified using multiple -injars options.
Source: http://proguard.sourceforge.net/index.html#manual/usage.html