I have created the .aar file (containing the resources & drawables) of an Android library project using
./gradlew assemble
I have enabled obfuscating by setting minify == true
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
However when I run the mentioned gradle command with minify enabled = true, I get java.io.IOException: The output jar is empty. Did you specify the proper '-keep' options?
What does this error pointing to and how can I obfuscate the library .aar file?
Best Regards
Using Proguard worked like a charm for me!
Proguard is used to shrink, obfuscate, optimize code. Proguard is
necessary for your library to remove unused code and make reverse
engineering little difficult. Proguard rules for the library are
different from the normal applications. As you know, Proguard renames
classes, variables, and methods using meaningless names. You would
like to keep the names of those methods and classes as it is that
developers will call. You will need to test and verify obfuscated code
from generated AAR file.
Library Module
build.gradle of your library
buildTypes {
release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true
// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
Inside of your proguard-rules.pro of your library
# Save the obfuscation mapping to a file, so we can de-obfuscate any stack
# traces later on. Keep a fixed source file attribute and all line number
# tables to get line numbers in the stack traces.
# You can comment this out if you're not interested in stack traces.
-printmapping out.map
-keepparameternames
-renamesourcefileattribute SourceFile
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod
# Preserve all annotations.
-keepattributes *Annotation*
# Preserve all public classes, and their public and protected fields and
# methods.
-keep public class * {
public protected *;
}
# Preserve all .class method names.
-keepclassmembernames class * {
java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String, boolean);
}
# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
native <methods>;
}
# Preserve the special static methods that are required in all enumeration
# classes.
-keepclassmembers class * extends java.lang.Enum {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
# You can comment this out if your library doesn't use serialization.
# If your code contains serializable classes that have to be backward
# compatible, please refer to the manual.
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
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();
}
# The library may contain more items that need to be preserved;
# typically classes that are dynamically created using Class.forName:
# -keep public class mypackage.MyClass
# -keep public interface mypackage.MyInterface
# -keep public class * implements mypackage.MyInterface
Thanks to .... reference
https://dev.to/mohitrajput987/develop--publish-your-own-sdk-in-android---part-2getting-started-with-sdk-development-3159
Proguard cuts unused classes. Libraries are standalone products, and has some specific entry points, which should not be obfsuscated. So you need to add rules to keep this entry points. Rules lookes like this:
-keep class packagename {public *;}
Copy library.pro file to your library project from this location:
...\android-sdk\tools\proguard\examples
Comment these lines when building from Android Studio, it should be probably kept/updated when building from the command line:
-injars in.jar
-outjars out.jar
-libraryjars /lib/rt.jar
Update your library project build.gradle file to use library.pro:
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'library.pro'
}
Sync and build the project and it should generate an obfuscated AAR file now.
Two suggestions:
Please ensure this file exist in the library module:
proguard-rules.pro.
Please try running "./gradlew :mylib:assembleRelease" with the
"mylib" be replaced with your library module name.
Related
I have an application which uses retrofit to fetch logo form an API.
When i don't obfuscate and shrink my code, everything works fine. But when i enable it, the API call stops working. I don't get any crash or error messages, i just don't get any values from the API.
gradle
buildTypes {
debug{
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true
// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
release {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
I used it in debug just for testing purposes. If i use it in release, it has same output.
I checked the retrofit page and they advised to use the following file
proguard-rules.pro
# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
-renamesourcefileattribute SourceFile
-printusage usage.txt
-keep class com.th3pl4gu3.locky_offline.core.main.** {*;}
-keep class com.th3pl4gu3.locky_offline.repository.network.** {*;}
-keep class com.th3pl4gu3.locky_offline.repository.database.** {*;}
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod
# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
#retrofit2.http.* <methods>;
}
# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit
# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { #retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>
The API call still doesn't work. I tried many posts on stackoverflow but nothing works for me. Can someone help me by proposing a solution or an alternative to this ?
Thank you
Have you tried to put #SerializedName to Object field?
public class YourJsonClass{
#SerializedName("username")
String username;
}
Use progaurd rules from the official retrofit site
https://github.com/square/retrofit/blob/master/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro
Sometimes issue seems to be from retrofit but it is not. It’s a GSON and ProGuard problem. To fix it you just have to add a -keep class configuration in your proguard rules of the package where your Pojo class is stored For example my API response and request classes are stored like this (package name may differ according to your app):
-keep class com.sample.myapp.model.request. { ; }
-keep class com.sample.myapp.model.model.response.* { ; }*
The negator (exclamation mark) in proguard should allow me to keep anthing but the apache libraries:
-keep class !org.apache.**
According to those answers. That's the way to go:
How to negate classname with Proguard
Enable Proguard for only two packages in large Android application
Android proguard Ignore All Classes except of one
Proguard Android do not obfuscate anything except few classes
Proguard: How to keep everything except specific condition?
Can we shrink all classes but only obfuscate some with proguard?
However, it obfuscates all classes in my APK.
That's part of my build.gradle (I have Android Studio 3.5.3)
compileSdkVersion 29
buildToolsVersion "29.0.2"
//...
buildTypes {
release {
minifyEnabled true
proguardFiles /*getDefaultProguardFile('proguard-android.txt'),*/ 'proguard-rules.pro'
// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
shrinkResources false
}
}
dependencies {
//Utility libs
implementation 'org.apache.commons:commons-collections4:4.1'
implementation 'org.apache.commons:commons-lang3:3.4'
implementation group: 'commons-io', name: 'commons-io', version: '2.5'
}
After I added -printconfiguration to my proguard-rules.pro file I saw there are numerous -keep rules following my -keep class !org.apache.**
-printconfiguration
-keep class !org.apache.**
# Referenced at ***anonymized***\app\build\intermediates\merged_manifests\release\AndroidManifest.xml:180
-keep class android.support.v4.app.CoreComponentFactory { <init>(); }
# Referenced at ***anonymized***\app\build\intermediates\merged_manifests\release\AndroidManifest.xml:180
-keep class com.mycompany.MyApplication { <init>(); }
# Referenced at C:\Users\***anonymized***\.gradle\caches\transforms-2\files-2.1\7f5f0b3369d8fa8a72a20e2278ec0acc\appcompat-v7-28.0.0\res\layout\abc_action_menu_item_layout.xml:17
-keep class android.support.v7.view.menu.ActionMenuItemView { <init>(...); }
That approach suggested by Ezekiel Baniaga also didn't work. Instead it keeps everything including the apache packages:
proguard-rules.pro
-printconfiguration
-dontshrink
-dontoptimize
-dontobfuscate
-keep,allowshrinking,allowoptimization,allowobfuscation class org.apache.**
I had to add ,** to get it working. Thanks T. Neidhart!
-keep class !org.apache.**,**
The previous example preserved class names but still obfuscated members. So I had to add { *; }:
-keep class !org.apache.**,** { *; }
That's how I obfuscate multiple packages (I have to use them all in one keep rule!)
-keep class !org.apache.**, !org.zeroturnaround.**, !com.drew.**, ** { *; }
To find out what my problem is with -dontshrink -dontoptimize -dontobfuscate -keep,allowshrinking,allowoptimization,allowobfuscation class org.apache.** I could add -whyareyoukeeping according to https://www.guardsquare.com/en/products/proguard/manual/usage
You should file a bug report with the R8 project if this does not work anymore.
In order to keep using Proguard in the meantime, you can add this to your gradle.properties files:
android.enableR8=false
Further tests show that the implicit behavior of ProGuard is not implemented like that in R8.
So a rule like:
-keep class !org.apache.**
will implicitly keep all other classes when using ProGuard, but not when using R8. To achieve the same behavior with R8, change the rule to this:
-keep class !org.apache.**,**
I recently changed 2 lines of code as a workaround due to changes to the data being returned from an API I'm using. Now the app is crashing when using the release apk and aab. However, when I'm using the app through the Android Emulator on API 27 and connecting an API 27 device to my computer running the debug apk, the app works flawlessly.
I'm really stumped on this problem, and do not understand the error messages at all.
FATAL EXCEPTION: main
Process: com.guy.aqi, PID: 8328
java.lang.NullPointerException: throw with null exception
at com.guy.aqi.n.a(Unknown Source:3)
at com.guy.aqi.m.b(CurrentAirQualityFragment.java:8)
at com.guy.aqi.m.b(CurrentAirQualityFragment.java:6)
at com.guy.aqi.d.a(Unknown Source:4)
at b.a.a.a.m.c(StringRequest.java:4)
at b.a.a.a.m.a(StringRequest.java:1)
at b.a.a.h$a.run(ExecutorDelivery.java:4)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
My API stopped sending the "main pollutant" String, so I changed this line:
textViewMainPollutantUS.setText("U.S. Main Pollutant: " decodePollutant(mainPollutantUS));
to
textViewMainPollutantUS.setText("");
and this line:
textViewMainPollutantCN.setText("China Main Pollutant: " decodePollutant(mainPollutantCN));
to
textViewMainPollutantCN.setText("");
I expected changing these lines would fix the issue. But now the issue seems to be fixed in debug version of the app, but not the release version.
proguard-rules.pro
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform
# Prevent Proguard from inlining methods that are intentionally extracted to ensure locals have a
# constrained liveness scope by the GC. This is needed to avoid keeping previous request references
# alive for an indeterminate amount of time. See also https://github.com/google/volley/issues/114
-keepclassmembers,allowshrinking,allowobfuscation class com.android.volley.NetworkDispatcher {
void processRequest();
}
-keepclassmembers,allowshrinking,allowobfuscation class com.android.volley.CacheDispatcher {
void processRequest();
}
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON #Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in #JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
##---------------End: proguard configuration for Gson ----------
-keep class com.crashlytics.** { *; }
-keepattributes SourceFile,LineNumberTable
For me, adding lines (you may have other name for package where you put your POJO files):
-keep class [mypackagename].model.** { *; }
-keep class [mypackagename].datamodel.** { *; }
to proguard.rules worked perfectly, then options:
android
{
...
buildTypes {
release {
minifyEnabled true
shrinkResources true
}
}
}
are set in build.gradle (Module: app)
Edit:-
Replace this
minifyEnabled true
to
minifyEnabled false
I'm trying to create an obfuscation library using a class from my project.
So far, I managed to create a library ('aar' file), which contain that class.
I checked it by deleting the class from the project and importing the library.
My next step is to obfuscate the library I created.
In the library's gradle I wrote:
buildTypes {
release {
minifyEnabled true
useProguard true
consumerProguardFiles 'proguard-rules.pro'
}
}
in addition, I used the example ProGuard rules file:
#
# This ProGuard configuration file illustrates how to process a program
# library, such that it remains usable as a library.
# Usage:
# java -jar proguard.jar #library.pro
#
# Specify the input jars, output jars, and library jars.
# In this case, the input jar is the program library that we want to process.
#-injars in.jar
#-outjars out.jar
#
#-libraryjars <java.home>/lib/rt.jar
# Save the obfuscation mapping to a file, so we can de-obfuscate any stack
# traces later on. Keep a fixed source file attribute and all line number
# tables to get line numbers in the stack traces.
# You can comment this out if you're not interested in stack traces.
-printmapping out.map
-keepparameternames
-renamesourcefileattribute SourceFile
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod
# Preserve all annotations.
-keepattributes *Annotation*
# Preserve all public classes, and their public and protected fields and
# methods.
-keep public class * {
public protected *;
}
# Preserve all .class method names.
-keepclassmembernames class * {
java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String, boolean);
}
# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
native <methods>;
}
# Preserve the special static methods that are required in all enumeration
# classes.
-keepclassmembers class * extends java.lang.Enum {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
# You can comment this out if your library doesn't use serialization.
# If your code contains serializable classes that have to be backward
# compatible, please refer to the manual.
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
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();
}
# Your library may contain more items that need to be preserved;
# typically classes that are dynamically created using Class.forName:
# -keep public class mypackage.MyClass
# -keep public interface mypackage.MyInterface
# -keep public class * implements mypackage.MyInterface
Now, when I import the library, the project can't find the required class from the library - "cannot resolve symbol 'classname'".
When I write 'false' in the minifyEnabled, the project does recognize the class from the library but the class is not obfuscated. probably because it turns the ProGuard feature off.
I'm stuck at this step, I need the library class to be obfuscated when de-compiled (change the variables name to something less readable). I don't know what am I doing wrong :/
Warning:library class org.apache.http.conn.ManagedClientConnection extends or implements program class org.apache.http.HttpClientConnection
despite having -dontwarn org.apache.** in my proguard rules file
Warning:org.joda.time.Weeks: can't find referenced class org.joda.convert.FromString
despite having -dontwarn org.joda.** in my proguard rules file, and -keep class org.** { *; } along with -keep interface org.** { *; }
Warning:there were 140 instances of library classes depending on program classes.
Error:Execution failed for task ':app:proguardRelease'.
> java.io.IOException: Please correct the above warnings first.
so then I look at my app structure
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-project.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
zipAlignEnabled true
}
}
proguard-project.txt is in the same folder as this build.gradle file in this module.
Not sure what is broken now, I guess there is something else that nobody told me about when I was required to update to Build Tools 21.1.1 to make other things work. any insight appreciated.
My solution was to copy all of my rules into proguard-rules.pro which the IDE had previously automatically generated for me, and then proguard worked as expected
I still don't know where getDefaultProguardFile('proguard-project.txt') is supposed to be located, but it is not being read, I moved it within my module and in the root folder of the project.