Config the build using Build Variants and Proguard
Configure Build Variants
In build.gradle
file you may define separate buildTypes like this:
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
debuggable false
versionNameSuffix "r"
}
debug {
versionNameSuffix "d"
debuggable true
}
test {
applicationIdSuffix ".test"
versionNameSuffix "t"
debuggable false
}
The above snippet achieves the following:
- config release, debug, and test builds
- set
versionNameSuffix
to refer to the version string which is build - set
applicationIdSuffix
for may install test and release build on one device - and most usefull Gradle Build Types can contribute to the build with resources: src/[buildtypename]/res. So you can have separate resource for separate builds.
For more info go to http://tools.android.com/tech-docs/new-build-system/user-guide
Seperateing Configs in Java File to gradle.properties
For example, I have a MyApplication.java
public class MyApplication extends Application {
private static final String PARSE_API_ID = "xxxxxxxxxxxxxxxxxxxxx";
private static final String PARSE_API_KEY = "xxxxxxxxxxxxxxxxxxxxx";
public MyApplication() {
super();
}
@Override
public void onCreate() {
super.onCreate();
Parse.initialize(this, PARSE_API_ID, PARSE_API_KEY);
ParseInstallation.getCurrentInstallation().saveInBackground();
}
}
Steps
#1. In gradle.properties
, add:
parseApiId=xxxxxxxxxxxxxxxxxxxxx
parseApiKey=xxxxxxxxxxxxxxxxxxxxx
#2. In build.gradle
, add:
buildTypes {
debug {
buildConfigField "String", "PARSE_API_ID", "\"${project.property("parseApiId")}\""
buildConfigField "String", "PARSE_API_KEY", "\"${project.property("parseApiKey")}\""
}
release {
buildConfigField "String", "PARSE_API_ID", "\"${project.property("parseApiId")}\""
buildConfigField "String", "PARSE_API_KEY", "\"${project.property("parseApiKey")}\""
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
#3. After syncing build.gradle
file, BuildConfig.java will be generated automatically. BuildConfig.java
would be like this:
public final class BuildConfig {
// Fields from build type: debug
public static final String PARSE_API_ID = "xxxxxxxxxxxxxxxxxxxxx";
public static final String PARSE_API_KEY = "xxxxxxxxxxxxxxxxxxxxx";
}
#4. Apply to your MyApplication.java
like:
public class MyApplication extends Application {
public MyApplication() {
super();
}
@Override
public void onCreate() {
super.onCreate();
Parse.initialize(this, BuildConfig.PARSE_API_ID, BuildConfig.PARSE_API_KEY);
ParseInstallation.getCurrentInstallation().saveInBackground();
}
}
Configure the Build Process to Automatically Sign Your APK
#1. In your gradle.properties
file, add:
# Signing configs
RELEASE_STORE_FILE = ../your_keystore.jks
RELEASE_STORE_PASSWORD = xxxx
RELEASE_KEY_ALIAS = xxxx
RELEASE_KEY_PASSWORD = xxxx
#2. Make sure your_keystore.jks
file is under the root path of the project.
#3. In the build.gradle
file, add:
signingConfigs {
release {
storeFile file(project.property("RELEASE_STORE_FILE"))
storePassword project.property("RELEASE_STORE_PASSWORD")
keyAlias project.property("RELEASE_KEY_ALIAS")
keyPassword project.property("RELEASE_KEY_PASSWORD")
}
and
buildTypes {
release {
signingConfig signingConfigs.release
}
Make sure the code block of signingConfigs is above the code block of buildTypes, otherwise it cannot find signingConfigs.release
when syncing the gradle.
#4. Open the Build Variants tool window and ensure that the release build type is selected.
#5. Click Build > Build APK to build your release build, and confirm that Android Studio has created a signed APK in the build/outputs/apk/
directory for your module.
There you go! You will see
- app-release-unaligned.apk
- app-release.apk
under the path of build/outputs/apk/
.
Shrink Your Code and Resources
To enable code shrinking with ProGuard, add minifyEnabled true
to the appropriate build type in your build.gradle
file. The proguardFiles
property defines the ProGuard rules:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(‘proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}
Resource shrinking works only in conjunction with code shrinking. After the code shrinker removes all unused code, the resource shrinker can identify which resources the app still uses.
To enable resource shrinking, set the shrinkResources
property to true
in your build.gradle
file (alongside minifyEnabled
for code shrinking):
android {
...
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
Troubles I Met
1. After enabling Proguard
, the proguard warning said that can’t find referenced class and can’t find referenced method.
The class that was pointed out is part of the Java runtime (rt.jar) but not part of the Android runtime (android.jar), so ProGuard warns that something might be broken. If you’re sure that your application works anyway, you can specify
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn java.nio.file.**
-dontwarn java.lang.invoke.**
-dontwarn java.lang.reflect.**
in your Proguard config file. Here I added this in proguard-rules.pro
.
Glance at Stackoverflow
- proguard hell - can’t find referenced class
- how to fix proguard warning ‘can’t find referenced method’ for existing methods ‘clone’ and ‘finalize’ of class java.lang.Object
- how to fix proguard warning ‘can’t find referenced method’ for existing methods ‘clone’ and ‘finalize’ of class java.lang.Object
2. Installing a signed release build with Proguard enabled into the device, after some operations getting crash error: Proguard Missing type parameter at at java.lang.reflect.Method.invoke.
google/gson
has their example of proguard configuration for android applications.
They propose to use this template of configuration:
##---------------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
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }
##---------------End: proguard configuration for Gson ----------
For our case we need to add this specifically, otherwise we get at net.magellan.f.run(Unknown Source) at java.util.Timer$TimerImpl.run with Missing type parameter.
-keep class net.magellan.** { *; }
Glance at Stackoverflow
Speeding up Gradle build time
#1. Create a file named gradle.properties
in the following directory:
- /home/
/.gradle/ (Linux) - /Users/
/.gradle/ (Mac) - C:\Users<username>.gradle (Windows)
#2. Add this line to the file:
org.gradle.daemon=true
DO NOT OVERLY MATCH YOUR PROGUARD RULE
@jakewharton gave a talk on Aug 24, 2016, saying that
Don’t use overly matchy rules. If you ever see * * in a ProGuard rules file, that is wrong 100% of the time. That is never what you should be using because you’ve effectively just nuked any advantages that you were getting from ProGuard.
Our proguard rule seems a bit inappropriate due to its overly match rules. But once I cross out the rule below:
#-keep class net.magellan.** { *; }
The app will crash with this message:
E/AndroidRuntime: FATAL EXCEPTION: Timer-5
Process: planet.co.jp.multilingualscanner, PID: 30833
java.lang.NoSuchMethodError: no static method "Lnet/magellan/Manager;._workerResponceCallback(JII[B[B)V"
at net.magellan.Manager.pollWorkerNativeEvent(Native Method)
at net.magellan.c.run(SourceFile:259)
at java.util.Timer$TimerImpl.run(Timer.java:284)
and
E/AndroidRuntime: FATAL EXCEPTION: Timer-0
Process: planet.co.jp.multilingualscanner, PID: 7274
java.lang.NoSuchMethodError: no static method "Lnet/magellan/Manager;._workerErrorCallback(JII[B[B)V"
at net.magellan.Manager.pollWorkerNativeEvent(Native Method)
at net.magellan.c.run(SourceFile:259)
at java.util.Timer$TimerImpl.run(Timer.java:284)
Add this to be more specific:
-keep class net.magellan.Manager { public static void _workerResponceCallback(long, int , int , byte[] , byte[] ); }
-keep class net.magellan.Manager { public static void _workerErrorCallback(long, int, int, byte[], byte[]); }
Today’s Breakfest
Reference
Configure Build Variants
Shrink Your Code and Resources
gradle.propertiesに定義した値をJavaソースから参照する
Handling signing configs with Gradle
Sign Your App
How To… Speed up Gradle build time