I am using android studio bumble bee
I created a module library which has a single class
class Lib2 {
fun print(){
println("From lib2")
}
}
The proguard as
-keepclasseswithmembers class com.example.lib2.Lib2{
public <methods>;
public <fields>;
}
I create the release aar with minifyEnabled true
However when I integrate the aar with my app module I am not getting Lib2. What is wrong here?
From documentation:
Specifies classes and class members to be preserved, on the condition that all of the specified class members are present. For example, you may want to keep all applications that have a main method, without having to list them explicitly.
In English it means "Don't obfuscate classes that have the following methods and fields structure".
For example, keeping all classes which have constructors with single Context parameter:
-keepclasseswithmembers class * {
public <init>(android.content.Context);
}
Since, from my understanding, you simply want to keep the whole class, you can use:
-keep public class com.example.lib2.Lib2
In my opinion, it's better to use #Keep annnotation directly in the class, because:
The proguard rules is decoupled from source and as the time flies we forget that the rule is there. by using #Keep you know immediately what classes are being kept from obfuscation and it's much more readable.
This annotation is packed with your AAR so when you publish it, consumer apps won't obfuscate it as well. With proguard rules you must add another rules file to pack with your AAR that "tells" the consumer app to not obfuscate this class.
Much more simple:
#Keep
class Lib2 {
fun print(){
println("From lib2")
}
}
Related
Using ProGuard to keep an entry point in a library, and also not allow it to be obfuscated, I use the proguard keep rule:
-keep,includedescriptorclasses public class com.demo.api.** { *; }
I would like to replace this rule with a #Keep annotation like this:
#Keep
public class SomeClass {
public void someMethod() { /*..*/ }
}
If I analyze the aar library containing this class, the SomeClass was kept (not shrunk) and not obfuscated along with method someMethod, as expected.
If I build an app using this library, however, the class SomeClass is kept, but its members are still obfuscated. The app only has the default rules
and an empty local proguard_rules.txt:
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
How can I get the #Keep annotation behave in a similar way as the ProGuard -keep rule?
The #keep annotation is added to the class and it will not automatically inherit the keep behavior to all its methods. All the Keep annotation does is ensure the class is not minified (removed) and not obfuscated. So to ensure the methods are not obfuscated or minified the #Keep annotation has to be added to all the methods you want retained as well.
Occasionally after generating the Signed APK, the following warning would appear
Missing class: com.google.android.aidl.BaseProxy
Missing Class: com.google.android.aidl.BaseStub
However, the APK would be successfully generated. Only when released the warning would be detrimental to the app.
Fatal Exception: java.lang.NoClassDefFoundError
Failed resolution of: Lcom/google/android/aidl/BaseStub
What gradle dependancy is required so this class is found and resolved?
Here are links to my gradle files (shared on google drive):
build.gradle (module: app)
build.gradle (project)
Thanks.
Try to update your proguard rules with the following:
-keepclassmembers class com.google.android.aidl.** { *; }
EDIT: (from proguard documentation)
-keep: Specifies classes and class members (fields and methods) to be preserved as entry points to your code.
-keepclassmembers: Specifies class members (only) to be preserved, if their classes are preserved as well.
If you specify a class, without class members, ProGuard only preserves the class and its parameterless constructor as entry points. It may still remove, optimize, or obfuscate its other class members.
If you specify a method, ProGuard only preserves the method as an entry point. Its code may still be optimized and adapted.
So if you're not sure which option you need, you should probably simply use -keep. It will make sure the specified classes and class members are not removed in the shrinking step, and not renamed in the obfuscation step.
(below -keep includes all classes and class members from aidl)
-keep class com.google.android.aidl.** { *; }
In your case you are missing BaseProxy and BaseStub classes. You can specify only these classes in your -keep and -keepclassmembers and test which method is suitable for you with best code obfuscation for your release build.
(below -keep includes only BaseProxy and BaseStub)
-keep class com.google.android.aidl.BaseProxy { *; }
-keep class com.google.android.aidl.BaseStub { *; }
My suggestion is to specify the class names you don't want to remove and utilize the code obfuscation to reduce your app size.
The symptoms of your issue (only happens in release build means proguard is removing the class) leads me to suggest :
if the class missing is one of yours add this annotation to the that class
#Keep class TheClass { ... }
if the class giving you pain is in the third party lib (mostly you add lib via gradle file in your project ) then normally in the library readme file (from their website like Github repo readme etc ) there is a proguard rules note that you need to add something like :
# Parceler library
-keep interface org.parceler.Parcel
-keep #org.parceler.Parcel class * { *; }
-keep class **$$Parcelable { *; }
Proguard obfuscation renames the methods and classes of my android source after the exportation, I need that a specific method in a specific class mantain is name also after the build with proguard.
How could I perform this?
For example:
assuming that I want to preserve the name of the method myMethod in the class MyClass of package my.package.android.com How should I write the -keep modifier?
You should create ProGuard config file using -keep option with specified class name you want to be ommited during obfuscating.
See ProGuard docs: http://proguard.sourceforge.net/index.html#manual/usage.html
-keep [,modifier,...] class_specification
Specifies classes and class members (fields and methods) to be preserved as entry points to your code.
I'm struggling with the setup of Dagger (1.0.1), in a existing application. It was configured to use ProGuard but I disabled it for this test with -dontobfuscate.
When I enable dagger-compiler it's able to successfully generate a dot file with the dependencies graph, but when I remove the compiler and build the app in Release mode it crashes during startup, complaining that it's unable to create the object graph.
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.corp.myapp/com.corp.myapp.ui.activity.MainActivity}:
java.lang.IllegalStateException: Errors creating object graph:
No injectable members on com.corp.myapp.core.services.ConnectionMonitor. Do
you want to add an injectable constructor? required by
com.corp.myapp.core.services.ConnectionMonitor
com.corp.myapp.ui.activity.MyAppBaseActivity.connectionManager
No injectable members on com.corp.myapp.ui.crouton.CroutonManager. Do you want
to add an injectable constructor? required by
com.corp.myapp.ui.crouton.CroutonManager
com.corp.myapp.ui.activity.MyAppBaseActivity.croutonManager
No injectable members on com.corp.core.assembler.ResourceAssembler. Do you want
to add an injectable constructor? required by
com.corp.core.assembler.ResourceAssembler
com.corp.myapp.ui.activity.MyAppBaseActivity.resourceAssembler
I see MyAppBaseActivity and it's dependencies with CroutonManager or ConnectionMonitor being displayed in the generated dot file, so according to this comment I expected this to work. AFAIK if there was something wrong it should be detected by the compiler-enabled build that I used to generate the dot file.
UPDATE:
I previously stated that
In Debug mode it never fails
but it's not really true after further testing: In Debug mode it doesn't fail because ProGuard is disabled, whereas in Release mode it is enabled by default. If I build the app in Release mode but skip ProGuard, I don't get the errors either and the app successfully starts. So the problem is definitely related to my ProGuard configuration.
Dagger relies a lot on reflection and class names that are hard-coded and manipulated as strings. This makes the code difficult to shrink/optimize/obfuscate.
The following configuration works for the sample dagger/examples/simple in Dagger 1.1.0:
-keepattributes *Annotation*
-keepclassmembers,allowobfuscation class * {
#javax.inject.* *;
#dagger.* *;
<init>();
}
-keep class **$$ModuleAdapter
-keep class **$$InjectAdapter
-keep class **$$StaticInjection
-keepnames !abstract class coffee.*
-keepnames class dagger.Lazy
The configuration keeps all fields and methods with javax.inject or dagger annotations, and all parameterless constructors. ProGuard might otherwise remove them if they appear unused, but Dagger is actually injecting/accessing them through reflection. This is similar to RoboGuice.
It also has to keep all adapter classes generated by Dagger.
It also has to keep all class names related to these adapter classes, so the names still match. In this sample, those are almost all classes in the package coffee, so the easiest way is to use a wild-card. This line will be different for other applications.
Finally, it also has to keep the name of the class dagger.Lazy, since its name is hard-coded as a string in the generated code.
I got the app to start after adding -dontshrink to the ProGuard config file. Having -dontobfuscate at the beginning was not enough.
In fact, if I remove -dontobfuscate it also works.
I definitely need finer control for this but it's a starting point. My current ProGuard setup for Dagger is:
#############
# Dagger #
#############
-keep class dagger.** { *; }
-dontwarn dagger.internal.codegen.**
Dagger doesn't require #Inject to be on a class to be passed into graph.inject(myActivity) because some activities may not have any injections to make. However, these seem like upstream dependencies, which means that they need to be provided to ComponentInfo, and therefore need to be provisioned by Dagger. It cannot do this if it cannot create these classes, and it can't do so if these are not annotated, unless it provides them via a #Provides method.
So, you either need to create an #Module-annotated class which returns these types from #Provides-annotated methods, or you need to add #Inject to their constructor.
-keep class * extends dagger.internal.Binding
That said, in this case, are you using proguard in "release" mode? And not proguarding in debug mode? If so, I suspect Proguard to be stripping away annotations. You'll need to do some variant of:
-keep class javax.inject.** { *; }
... to ensure that Proguard doesn't remove the annotations.
I ended up burning a week+ trying to get this to work. In the end I failed and decided to give DexGuard a shot. It worked beautifully right out of the box. Yes its a commercial product but DexGuard has great support and because of such we were able to finally ship. Id definitely recommend DexGuard if you absolutely need to solve this issue.
Let's say I got an android app project with tests.
Is there any way we can run our test suite (in a separate test project) against the release version ?
After reading the bounty's comment, I realised OP actually ask something more than a simple Yes/No reply, so I am going to extend my comment to an answer. Generally speaking, a proper designed proguard.cfg and project structure is sufficient to prevent this dilemma.
A typical proguard configuration (see section 7. A complete Android application section in this link) guarantee that all android related stuff like Activity, View and etc. is preserved during obfuscation. It doesn't make any sense to alter the configuration for example, to obfuscate Acticity.onCreate() method as it will obviously ruin the application at runtime. In another word, a well design proguard.cfg will protect all public interface to the underlying runtime framework and keep them remain unchanged.
... ...
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
... ...
On the other hand, Android test project should focus on testing Android component (Intentionally preserved during obfuscation), i.e. a view is proerly rendered, a button click perform correct task, and should avoid writing tests for POJO class that doesn't rely on any Android API, note that these POJOs are what we obfuscate usually. It is better to write pure junit tests for these POJO in the application or referenced java project so that these junit tests are involved at maven test phase before creating the final release (obfuscated, signed and zipaligned). In addition, a good OO design will shield these intermediate POJO dependencies and make them transparent to the outside, i.e. the runtime framework.
app/
src/main/java/
src/test/java/ <-- intermediate POJO tests put here.
AndroidManifest.xml
... ...
app-test/
src/main/java <-- Android component tests put here.
AndroidManifest.xml
... ...
It is absolutely fine to write POJO junit tests inside Android test project, however, if you still want to keep the ability to run the test project against obfuscated apk, you need adjust application project's proguard.cfg properly and preserve the POJO class during obfuscation in order to suit the test code.
You can instruct proguard to write out the mapping it creates into a file using the -printmapping <filename> directive. The structure of that file is obvious and can be parsed into a Hashtable. Then I would write a script that does apply those conversions onto your tests (making copies of them). Obfuscation simply means replacing class and method names from something (human-readable and) valid in terms of the Java Bytecode Specification to something other (short and not human-readable but) also valid, so this should work. Compile the adapted tests against the obfuscated project and run them.
Do you have your tests and source in one project? if so I believe android maven will strip out the test code when you perform a release.
To fix this you will need to move the tests to a seperate project that links to your actual application (assuming you rely on code/assets of your actual project) then the seperate project will still be able to instrument against a release build of your app (assuming they are signed by the same key).