494 lines
21 KiB
Plaintext
Executable File
494 lines
21 KiB
Plaintext
Executable File
page.title=Shrink Your Code and Resources
|
||
parent.title=Tools
|
||
parent.link=index.html
|
||
page.metaDescription=Make your APK file smaller and more secure by shrinking your code and resources.
|
||
@jd:body
|
||
|
||
<div id="qv-wrapper">
|
||
<div id="qv">
|
||
<h2>In this document</h2>
|
||
<ol>
|
||
<li><a href="#shrink-code">Shrink Your Code</a>
|
||
<ol>
|
||
<li><a href="#keep-code">Customize which code to keep</a></li>
|
||
<li><a href="#decode-stack-trace">Decode an obfuscated stack trace</a></li>
|
||
</ol>
|
||
</li>
|
||
<li><a href="#shrink-resources">Shrink Your Resources</a>
|
||
<ol>
|
||
<li><a href="#keep-resources">Customize which resources to keep</a></li>
|
||
<li><a href="#strict-reference-checks">Enable strict reference checks</a></li>
|
||
<li><a href="#unused-alt-resources">Remove unused alternative resources</a></li>
|
||
<li><a href="#merge-resources">Merge duplicate resources</a></li>
|
||
<li><a href="#troubleshoot-resource-shrink">Troubleshoot resource shrinking</a></li>
|
||
</ol>
|
||
</li>
|
||
</ol>
|
||
|
||
<h2>See also</h2>
|
||
<ul>
|
||
<li>
|
||
<a class="external-link"
|
||
href="http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/introduction.html">ProGuard
|
||
Manual</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<p>To make your APK file as small as possible, you should enable shrinking for
|
||
your code and resources in your release build. This guide describes how to do
|
||
both and how to specify what to keep or discard during a build.</p>
|
||
|
||
<p>Code shrinking is available with ProGuard, which detects and removes unused
|
||
classes, fields, methods, and attributes from your packaged app, including
|
||
those from included code libraries (making it a valuable tool for working
|
||
around the <a href="{@docRoot}tools/building/multidex.html">64k reference
|
||
limit</a>). ProGuard also optimizes the bytecode, removes unused code
|
||
instructions, and obfuscates the remaining classes, fields, and methods with
|
||
short names. The obfuscated code makes your APK difficult to reverse engineer,
|
||
which is especially valuable when your app uses security-sensitive features,
|
||
such as <a href="{@docRoot}google/play/licensing/overview.html">licensing
|
||
verification</a>.</p>
|
||
|
||
<p>Resource shrinking is available with the Android Plugin for Gradle, which
|
||
removes unused resources from your packaged app, including unused resources in
|
||
code libraries. It works in conjunction with code shrinking such that once
|
||
unused code has been removed, any resources no longer referenced can be safely
|
||
removed as well.</p>
|
||
|
||
|
||
<p>Features in this document depend on:</p>
|
||
<ul>
|
||
<li><a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools</a> 25.0.10
|
||
or higher</li>
|
||
<li><a href="{@docRoot}tools/revisions/gradle-plugin.html">Android Plugin
|
||
for Gradle</a> 2.0.0 or higher</li>
|
||
</ul>
|
||
|
||
<h2 id="shrink-code">Shrink Your Code</h2>
|
||
|
||
<p>To enable code shrinking with ProGuard, add <code>minifyEnabled true</code>
|
||
to the appropriate build type in your <code>build.gradle</code> file.</p>
|
||
|
||
<p>Be aware that code shrinking slows down the build time, so you should avoid
|
||
using it on your debug build if possible. However, it's important that you
|
||
do enable code shrinking on your final APK used for testing, because it might
|
||
introduce bugs if you do not sufficiently <a href="#keep-code">customize which
|
||
code to keep</a>.</p>
|
||
|
||
<p>For example, the following snippet from a <code>build.gradle</code> file
|
||
enables code shrinking for the release build:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
android {
|
||
buildTypes {
|
||
release {
|
||
minifyEnabled true
|
||
proguardFiles getDefaultProguardFile(‘proguard-android.txt'),
|
||
'proguard-rules.pro</a>'
|
||
}
|
||
}
|
||
...
|
||
}
|
||
</pre>
|
||
|
||
<p class="note"><strong>Note</strong>: Android Studio disables ProGuard when
|
||
using <a href=
|
||
"{@docRoot}tools/building/building-studio.html#instant-run">Instant
|
||
Run</a>.</p>
|
||
|
||
<p>In addition to the <code>minifyEnabled</code> property, the
|
||
<code>proguardFiles</code> property defines the ProGuard rules:</p>
|
||
|
||
<ul>
|
||
<li>The <code>getDefaultProguardFile(‘proguard-android.txt')</code> method gets
|
||
the default ProGuard settings from the Android SDK <code>tools/proguard/</code>
|
||
folder.
|
||
<p><strong>Tip</strong>: For even more code shrinking, try the
|
||
<code>proguard-android-optimize.txt</code> file that's in the same location. It
|
||
includes the same ProGuard rules, but with other optimizations that perform
|
||
analysis at the bytecode level—inside and across methods—to reduce your APK
|
||
size further and help it run faster.</p>
|
||
</li>
|
||
<li>The <code>proguard-rules.pro</code> file is where you can add custom
|
||
ProGuard rules. By default, this file is located at the root of the module
|
||
(next to the <code>build.gradle</code> file).</li>
|
||
</ul>
|
||
|
||
<p>To add more ProGuard rules that are specific to each build variant, add
|
||
another <code>proguardFiles</code> property in the corresponding
|
||
<code>productFlavor</code> block. For example, the following Gradle file adds
|
||
<code>flavor2-rules.pro</code> to the <code>flavor2</code> product flavor. Now
|
||
<code>flavor2</code> uses all three ProGuard rules because those from the
|
||
<code>release</code> block are also applied.</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
android {
|
||
...
|
||
buildTypes {
|
||
release {
|
||
minifyEnabled true
|
||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||
'proguard-rules.pro'</span>
|
||
}
|
||
}
|
||
productFlavors {
|
||
flavor1 {
|
||
}
|
||
flavor2 {
|
||
proguardFile 'flavor2-rules.pro'
|
||
}
|
||
}
|
||
}
|
||
</pre>
|
||
|
||
<p>With each build, ProGuard outputs the following files:</p>
|
||
<dl>
|
||
<dt><code>dump.txt</code></dt>
|
||
<dd>Describes the internal structure of all the class files in the APK.</dd>
|
||
|
||
<dt><code>mapping.txt</code></dt>
|
||
<dd>Provides a translation between the original and obfuscated class, method, and
|
||
field names.</dd>
|
||
|
||
<dt><code>seeds.txt</code></dt>
|
||
<dd>Lists the classes and members that were not obfuscated.</dd>
|
||
|
||
<dt><code>usage.txt</code></dt>
|
||
<dd>Lists the code that was removed from the APK.</dd>
|
||
</dl>
|
||
|
||
<p>These files are saved at
|
||
<code><module-name>/build/outputs/mapping/release/</code>.</p>
|
||
|
||
|
||
<h3 id="keep-code">Customize which code to keep</h3>
|
||
|
||
<p>For some situations, the default ProGuard configuration file
|
||
(<code>proguard-android.txt</code>) is sufficient and ProGuard removes all—and
|
||
only—the unused code. However, many situations are difficult for ProGuard to
|
||
analyze correctly and it might remove code your app actually needs. Some
|
||
examples of when it might incorrectly remove code include:</p>
|
||
|
||
<ul>
|
||
<li>When your app references a class only from the
|
||
<code>AndroidManifest.xml</code> file</li>
|
||
<li>When your app calls a method from the Java Native Interface (JNI)</li>
|
||
<li>When your app manipulates code at runtime (such as with reflection or
|
||
introspection)</li>
|
||
</ul>
|
||
|
||
<p>Testing your app should reveal any errors caused by inappropriately removed
|
||
code, but you can also inspect what code was removed by reviewing the
|
||
<code>usage.txt</code> output file saved in
|
||
<code><module-name>/build/outputs/mapping/release/</code>.</p>
|
||
|
||
<p>To fix errors and force ProGuard to keep certain code, add a <code><a
|
||
href="https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/usage.html"
|
||
>-keep</a></code> line in the ProGuard configuration file. For example:</p>
|
||
|
||
<pre>
|
||
-keep public class MyClass
|
||
</pre>
|
||
|
||
<p>Alternatively, you can add the <code><a href=
|
||
"{@docRoot}reference/android/support/annotation/Keep.html">@Keep</a></code>
|
||
annotation to the code you want to keep. Adding <code>@Keep</code> on a class
|
||
keeps the entire class as-is. Adding it on a method or field will keep the
|
||
method/field (and it's name) as well as the class name intact. Note that this
|
||
annotation is available only when using the <a href=
|
||
"{@docRoot}tools/support-library/features.html#annotations">Annotations Support
|
||
Library</a>.</p>
|
||
|
||
|
||
<p>There are many considerations you should make when using the
|
||
<code>-keep</code> option; for more information about customizing your
|
||
configuration file, read the <a href=
|
||
"http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/introduction.html">
|
||
ProGuard Manual</a>. The <a href=
|
||
"http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/troubleshooting.html">
|
||
Troubleshooting</a> section outlines other common problems you might encounter
|
||
when your code gets stripped away.</p>
|
||
|
||
|
||
<h3 id="decode-stack-trace">Decode an obfuscated stack trace</h3>
|
||
|
||
<p>After ProGuard shrinks your code, reading a stack trace is difficult (if not
|
||
impossible) because the method names are obfuscated. Fortunately, ProGuard
|
||
creates a <code>mapping.txt</code> file each time it runs, which shows the
|
||
original class, method, and field names mapped to the obfuscated names.
|
||
ProGuard saves the file in the app
|
||
<code><module-name>/build/outputs/mapping/release/</code> directory.</p>
|
||
|
||
|
||
<p>Be aware that the <code>mapping.txt</code> file is overwritten every time
|
||
you create a release build with ProGuard, so you must carefully save a copy
|
||
each time you publish a new release. By retaining a copy of the
|
||
<code>mapping.txt</code> file for each release build, you'll be able to debug a
|
||
problem if a user submits an obfuscated stack trace from an older version of
|
||
your app.</p>
|
||
|
||
<p>When publishing your app on Google Play, you can upload the
|
||
<code>mapping.txt</code> file for each version of your APK. Then Google Play
|
||
will deobfuscate incoming stack traces from user-reported issues so you can
|
||
review them in the Google Play Developer Console. For more information, see the
|
||
Help Center article about how to <a href=
|
||
"https://support.google.com/googleplay/android-developer/answer/6295281">deobfuscate
|
||
crash stack traces</a>.</p>
|
||
|
||
<p>To convert an obfuscated stack trace to a readable one yourself, use the
|
||
<code>retrace</code> script (<code>retrace.bat</code> on Windows;
|
||
<code>retrace.sh</code> on Mac/Linux). It is located in the
|
||
<code><sdk-root>/tools/proguard/</code> directory. The script takes the
|
||
<code>mapping.txt</code> file and your stack trace, producing a new, readable
|
||
stack trace. The syntax for using the retrace tool is:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
|
||
</pre>
|
||
|
||
<p>For example:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
retrace.bat -verbose mapping.txt obfuscated_trace.txt
|
||
</pre>
|
||
|
||
<p>If you do not specify the stack trace file, the retrace tool reads from
|
||
standard input.</p>
|
||
|
||
<h2 id="shrink-resources">Shrink Your Resources</h2>
|
||
|
||
<p>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. This is especially true when you add code
|
||
libraries that include resources—you must remove unused library code so the
|
||
library resources become unreferenced and, thus, removable by the resource
|
||
shrinker.</p>
|
||
|
||
<p>To enable resource shrinking, set the <code>shrinkResources</code> property
|
||
to <code>true</code> in your <code>build.gradle</code> file (alongside
|
||
<code>minifyEnabled</code> for code shrinking). For example:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
android {
|
||
...
|
||
buildTypes {
|
||
release {
|
||
shrinkResources true
|
||
minifyEnabled true
|
||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||
'proguard-rules.pro'
|
||
}
|
||
}
|
||
}
|
||
</pre>
|
||
|
||
<p>If you haven't already built your app using <code>minifyEnabled</code> for
|
||
code shrinking, then try that before enabling <code>shrinkResources</code>,
|
||
because you might need to edit your <code>proguard-rules.pro</code> file to
|
||
keep classes or methods that are created or invoked dynamically before you
|
||
start removing resources.</p>
|
||
|
||
<p class="note"><strong>Note</strong>: The resource shrinker currently does not
|
||
remove resources defined in a <code>values/</code> folder (such as strings,
|
||
dimensions, styles, and colors). This is because the Android Asset Packaging
|
||
Tool (AAPT) does not allow the Gradle Plugin to specify predefined versions for
|
||
resources. For details, see <a href=
|
||
"https://code.google.com/p/android/issues/detail?id=70869">issue 70869</a>.</p>
|
||
|
||
|
||
<h3 id="keep-resources">Customize which resources to keep</h3>
|
||
|
||
<p>If there are specific resources you wish to keep or discard, create an XML
|
||
file in your project with a <code><resources></code> tag and specify each
|
||
resource to keep in the <code>tools:keep</code> attribute and each resource to
|
||
discard in the <code>tools:discard</code> attribute. Both attributes accept a
|
||
comma-separated list of resource names. You can use the asterisk character as a
|
||
wild card.</p>
|
||
|
||
<p>For example:</p>
|
||
|
||
<pre>
|
||
<?xml version=1.0" encoding="utf-8"?>
|
||
<resources xmlns:tools="http://schemas.android.com/tools"
|
||
tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
|
||
tools:discard="@layout/unused2" />
|
||
</pre>
|
||
|
||
<p>Save this file in your project resources, for example, at
|
||
<code>res/raw/keep.xml</code>. The build does not package this file into your
|
||
APK.</p>
|
||
|
||
<p>Specifying which resources to discard might seem silly when you could
|
||
instead delete them, but this can be useful when using build variants. For
|
||
example, you might put all your resources into the common project directory,
|
||
then create a different <code>keep.xml</code> file for each build variant when
|
||
you know that a given resource appears to be used in code (and therefore not
|
||
removed by the shrinker) but you know it actually won't be used for the given
|
||
build variant.</p>
|
||
|
||
<h3 id="strict-reference-checks">Enable strict reference checks</h3>
|
||
|
||
<p>Normally, the resource shrinker can accurately determine whether a resource
|
||
is used. However, if your code makes a call to {@link
|
||
android.content.res.Resources#getIdentifier(String,String,String)
|
||
Resources.getIdentifier()} (or if any of your libraries do that—the <a href=
|
||
"{@docRoot}tools/support-library/features.html#v7-appcompat">AppCompat</a>
|
||
library does), that means your code is looking up resource names based on
|
||
dynamically-generated strings. When you do this, the resource shrinker behaves
|
||
defensively by default and marks all resources with a matching name format as
|
||
potentially used and unavailable for removal.</p>
|
||
|
||
<p>For example, the following code causes all resources with the
|
||
<code>img_</code> prefix to be marked as used.</p>
|
||
|
||
<pre>
|
||
String name = String.format("img_%1d", angle + 1);
|
||
res = getResources().getIdentifier(name, "drawable", getPackageName());
|
||
</pre>
|
||
|
||
<p>The resource shrinker also looks through all the string constants in your
|
||
code, as well as various <code>res/raw/</code> resources, looking for resource
|
||
URLs in a format similar to
|
||
<code>file:///android_res/drawable//ic_plus_anim_016.png</code>. If it finds
|
||
strings like this or others that look like they could be used to construct URLs
|
||
like this, it doesn't remove them.</p>
|
||
|
||
<p>These are examples of the safe shrinking mode that is enabled by default.
|
||
You can, however, turn off this "better safe than sorry" handling, and specify
|
||
that the resource shrinker keep only resources that it's certain are used. To
|
||
do this, set <code>shrinkMode</code> to <code>strict</code> in the
|
||
<code>keep.xml</code> file, as follows:</p>
|
||
|
||
<pre>
|
||
<?xml version="1.0" encoding="utf-8"?>
|
||
<resources xmlns:tools="http://schemas.android.com/tools"
|
||
tools:shrinkMode="strict" />
|
||
</pre>
|
||
|
||
<p>If you do enable strict shrinking mode and your code also references
|
||
resources with dynamically-generated strings, as shown above, then you must
|
||
manually keep those resources using the <code>tools:keep</code> attribute.</p>
|
||
|
||
<h3 id="unused-alt-resources">Remove unused alternative resources</h3>
|
||
|
||
<p>The Gradle resource shrinker removes only resources that are not referenced
|
||
by your app code, which means it will not remove <a href=
|
||
"{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
|
||
alternative resources</a> for different device configurations. If necessary,
|
||
you can use the Android Gradle plugin's <code>resConfigs</code> property to
|
||
remove alternative resource files that your app does not need.</p>
|
||
|
||
<p>For example, if you are using a library that includes language resources
|
||
(such as AppCompat or Google Play Services), then your APK includes all
|
||
translated language strings for the messages in those libraries whether the
|
||
rest of your app is translated to the same languages or not. If you'd like to
|
||
keep only the languages that your app officially supports, you can specify
|
||
those languages using the <code>resConfig</code> property. Any resources for
|
||
languages not specified are removed.</p>
|
||
|
||
<p>The following snippet shows how to limit your language resources to just
|
||
English and French:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
android {
|
||
defaultConfig {
|
||
...
|
||
resConfigs "en", "fr"
|
||
}
|
||
}
|
||
</pre>
|
||
|
||
<p>To customize which screen density or ABI resources to include in your APK,
|
||
instead use <a href=
|
||
"http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK
|
||
splits</a> to build different APKs for different devices.</p>
|
||
|
||
<h3 id="merge-resources">Merge duplicate resources</h3>
|
||
|
||
<p>By default, Gradle also merges identically named resources, such as
|
||
drawables with the same name that might be in different resource folders. This
|
||
behavior is not controlled by the <code>shrinkResources</code> property and
|
||
cannot be disabled, because it is necessary to avoid errors when multiple
|
||
resources match the name your code is looking up.</p>
|
||
|
||
<p>Resource merging occurs only when two or more files share an identical
|
||
resource name, type, and qualifier. Gradle selects which file it considers to
|
||
be the best choice among the duplicates (based on a priority order described
|
||
below) and passes only that one resource to the AAPT for distribution in the
|
||
APK file.</p>
|
||
|
||
<p>Gradle looks for duplicate resources in the following locations:</p>
|
||
|
||
<ul>
|
||
<li>The main resources, associated with the main source set, generally
|
||
located in <code>src/main/res/</code>.</li>
|
||
<li>The variant overlays, from the build type and build flavors.</li>
|
||
<li>The library project dependencies.</li>
|
||
</ul>
|
||
|
||
<p>Gradle merges duplicate resources in the following cascading priority order:</p>
|
||
|
||
<p>Dependencies → Main → Build flavor → Build type</p>
|
||
|
||
<p>For example, if a duplicate resource appears in both your main resources and
|
||
a build flavor, Gradle selects the one in the build flavor.</p>
|
||
|
||
<p>If identical resources appear in the same source set, Gradle cannot merge
|
||
them and emits a resource merge error. This can happen if you define multiple
|
||
source sets in the <code>sourceSet</code> property of your
|
||
<code>build.gradle</code> file—for example if both <code>src/main/res/</code>
|
||
and <code>src/main/res2/</code> contain identical resources.</p>
|
||
|
||
<h3 id="troubleshoot-resource-shrink">Troubleshoot resource shrinking</h3>
|
||
|
||
<p>When you shrink resources, the Gradle Console shows a summary of the
|
||
resources that it removed from the app package. For example:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
:android:shrinkDebugResources
|
||
Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
|
||
:android:validateDebugSigning
|
||
</pre>
|
||
|
||
<p>Gradle also creates a diagnostic file named <code>resources.txt</code> in
|
||
<code><module-name>/build/outputs/mapping/release/</code> (the same
|
||
folder as ProGuard's output files). This file includes details such as which
|
||
resources reference other resources and which resources are used or
|
||
removed.</p>
|
||
|
||
<p>For example, to find out why <code>@drawable/ic_plus_anim_016</code> is
|
||
still in your APK, open the <code>resources.txt</code> file and search for that
|
||
file name. You might find that it's referenced from another resource, as
|
||
follows:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
|
||
16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016
|
||
</pre>
|
||
|
||
<p>You now need to know why <code>@drawable/add_schedule_fab_icon_anim</code>
|
||
is reachable—and if you search upwards you'll find that resource is listed
|
||
under "The root reachable resources are:". This means there is a code reference
|
||
to <code>add_schedule_fab_icon_anim</code> (that is, its R.drawable ID was
|
||
found in the reachable code).</p>
|
||
|
||
<p>If you are not using strict checking, resource IDs can be marked as reachable
|
||
if there are string constants that look like they might be used to construct
|
||
resource names for dynamically loaded resources. In that case, if you search
|
||
the build output for the resource name, you might find a message like this:</p>
|
||
|
||
<pre class="no-pretty-print">
|
||
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
|
||
used because it format-string matches string pool constant ic_plus_anim_%1$d.
|
||
</pre>
|
||
|
||
<p>If you see one of these strings and you are certain that the string is not
|
||
being used to load the given resource dynamically, you can use the
|
||
<code>tools:discard</code> attribute to inform the build system to remove it,
|
||
as described in the section about how to <a href="#keep-resources"
|
||
>customize which resources to keep</a>.</p>
|