Getting Started with ProGuard
In this Android tutorial, you’ll learn how to strip down your app size by making use of ProGuard – an app shrinking and obfuscation tool. By Kolin Stürt.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started with ProGuard
30 mins
- Getting Started
- Using the APK Analyzer
- Adding the BubblePicker Library
- Enabling ProGuard
- Adding “Don’t Warn” Rules
- Adding BubblePicker Code
- Debugging with ProGuard Output Files
- Adding Keep Rules
- Adding Data to the BubblePicker
- Introspection and Reflection
- Enabling Advanced Optimizations
- Understanding Obfuscation
- Adding Annotations
- Where To Go From Here
Adding BubblePicker Code
In the res/layout/activity_main.xml file, replace the second TextView
(descriptionTextView
) with the following:
<com.igalata.bubblepicker.rendering.BubblePicker
android:id="@+id/picker"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:backgroundColor="@android:color/white" />
This adds the BubblePicker
to the main layout. In the MainActivity.kt file, add the following to the setupBubblePicker()
method:
picker.bubbleSize = 50
picker.centerImmediately = true
picker.adapter = object : BubblePickerAdapter {
val colors = resources.obtainTypedArray(R.array.colors)
val titles = listOf("1", "2", "3", "4", "5", "6")
val multiplier = 2
val modulus = 8
val addition = 1
override val totalCount = titles.size // 1
override fun getItem(position: Int): PickerItem { // 2
return PickerItem().apply {
title = titles[position]
val start = colors.getColor((position * multiplier) % modulus,0)
val end = colors.getColor((position * multiplier) % modulus + addition,0)
gradient = BubbleGradient(start, end, BubbleGradient.VERTICAL)
textColor = ContextCompat.getColor(this@MainActivity, android.R.color.white)
}
}
}
picker.listener = object : BubblePickerListener { // 3
override fun onBubbleSelected(item: PickerItem) {
}
override fun onBubbleDeselected(item: PickerItem) {
}
}
Note: This tutorial assumes you’re familiar with handling imports. If you don’t have on-the-fly imports set up, import by pressing option + return on a Mac or Alt + Enter on a PC while your cursor is over the item that needs to be imported.
Note: This tutorial assumes you’re familiar with handling imports. If you don’t have on-the-fly imports set up, import by pressing option + return on a Mac or Alt + Enter on a PC while your cursor is over the item that needs to be imported.
Here’s what’s going on in the updated method:
- You are overriding
totalCount
to tell theBubblePicker
how many bubbles there will be. - You’re overriding
getItem()
to return a customizedPickerItem
. - You set up the
BubblePickerListener
to handle selecting a bubble.
Finish off the implementation by adding this to the end of onResume
:
picker.onResume()
Add this to the end of onPause
:
picker.onPause()
Build and run the app. Uh oh — the app crashes with a NullPointerException
! Note that several methods in your stack trace are obfuscated – the names are changed and minified. This is one of the key features of ProGuard.
Check the output log to narrow down what the problem is. You can see in the Run tab that it has something to do with “GL” and “onDrawFrame” in the BubblePicker
library.
Note: It’s always good practice to add sufficient logging in your catch statements, nullability checks and error states. With ProGuard, this is crucial – in the event of a problem, it will help lead you or other developers to the root of the issue, especially when the method names in stack traces are obfuscated.
Note: It’s always good practice to add sufficient logging in your catch statements, nullability checks and error states. With ProGuard, this is crucial – in the event of a problem, it will help lead you or other developers to the root of the issue, especially when the method names in stack traces are obfuscated.
Debugging with ProGuard Output Files
When ProGuard finishes running, it produces 4 output files. They are:
- usage.txt – Lists code that ProGuard removed.
- dump.txt – Describes the structure of the class files in your APK.
- seeds.txt – Lists the classes and members that were not obfuscated. Good to verify that you have obfuscated your important files.
- mapping.txt – The file that maps the obfuscated names back to the original.
You can use the mapping file to find the culprit of the crash.
Run the APK Analyzer again, then select the classes.dex file. Your crash points to obfuscated code looking something like org.a.d.l.a. The single characters you see may vary from this example but you can follow the directory path of the characters:
At first, it’s not clear what the directories are. Click the Load Proguard mappings… button to map the obfuscated names back to the original.
Select the mapping.txt file in the debug folder and click OK.
Toggle the Deobfuscate names button to the left of the Change ProGuard mappings… button to switch between obfuscated and deobfuscated code. You can trace the problem down to the jbox2d
library.
You need to preserve jbox2d
so that ProGuard doesn’t muck with it.
There are a few more things you should know about the mappings file:
- Every time you make a release build, the mapping.txt file is rewritten. That means you must save each copy with each release of your app. That way, when you receive an obfuscated stack trace for a particular app release, it will be useful.
- You can upload your mapping.txt file to Google Play to deobfuscate your crash stack traces.
- If you’re using Fabric, a deployment and crash reporting tool from Google, instructions for uploading your mapping.txt file are here.
Adding Keep Rules
Keep rules tell ProGuard not to obfuscate certain parts of your code. Some options are:
-
keep
– Preserve entire class and class members. -
keepclassmembers
– Preserve the class members. -
keepclasseswithmembers
– Preserve all classes that have a specified member.
ProGuard rules are written in a specific template format. It’s best practice to use explicit keep rules, rather than keeping the entire class. Instead of preserving the entire BubblePicker
library, you only need to keep org.jbox2d
and its sub-packages. The format is the same as the dontwarn
rules.
Add the following to the proguard-rules.pro file right after the dontwarn
rules you added:
-keep class org.jbox2d.** { *; }
Inside the curly braces, you told ProGuard to match any method name.
Build and run the app. When your app loads up, you should see the bubbles floating around on the screen.
Note: If you’re sharing your code, write keep rules as you write your code and be sure to publish them on your site or GitHub README page so other developers can easily use your code without any problems.
Note: If you’re sharing your code, write keep rules as you write your code and be sure to publish them on your site or GitHub README page so other developers can easily use your code without any problems.
Now you’ll set up some real sloth data.