Class responsible for searching through a Gradle built tree (after resource merging, compilation and ProGuarding has been completed, but before final .apk assembly), which figures out which resources if any are unused, and removes them.
It does this by examining
A resource is referenced in code if either the field R.type.name is referenced (which
is the case for non-final resource references, e.g. in libraries), or if the corresponding
int value is referenced (for final resource values). We check this by looking at the
ProGuard output classes with an ASM visitor. One complication is that code can also
call Resources#getIdentifier(String,String,String)
where they can pass in the names
of resources to look up. To handle this scenario, we use the ClassVisitor to see if
there are any calls to the specific Resources#getIdentifier
method. If not,
great, the usage analysis is completely accurate. If we do find one, we check
all the string constants found anywhere in the app, and look to see if any look
relevant. For example, if we find the string "string/foo" or "my.pkg:string/foo", we
will then mark the string resource named foo (if any) as potentially used. Similarly,
if we find just "foo" or "/foo", we will mark all resources named "foo" as
potentially used. However, if the string is "bar/foo" or " foo " these strings are
ignored. This means we can potentially miss resources usages where the resource name
is completed computed (e.g. by concatenating individual characters or taking substrings
of strings that do not look like resource names), but that seems extremely unlikely
to be a real-world scenario.
For now, for reasons detailed in the code, this only applies to file-based resources like layouts, menus and drawables, not value-based resources like strings and dimensions.
Modifiers | Name | Description |
---|---|---|
static class |
ResourceUsageAnalyzer.Resource |
Modifiers | Name | Description |
---|---|---|
static boolean |
TWO_PASS_AAPT |
Whether we support running aapt twice, to regenerate the resources.arsc file such that we can strip out value resources as well. |
static int |
TYPICAL_RESOURCE_COUNT |
Type | Name and description |
---|---|
void |
analyze() |
static String |
getFieldName(String styleName) |
int |
getUnusedResourceCount() |
boolean |
isDebug() |
boolean |
isDryRun() |
boolean |
isVerbose() |
void |
removeUnused(File destination) Remove resources (already identified by analyze()). |
void |
rewriteResourceZip(File source, File dest) "Removes" resources from an .ap_ file by writing it out while filtering out unused resources. |
void |
setDebug(boolean verbose) |
void |
setDryRun(boolean dryRun) |
void |
setVerbose(boolean verbose) |
Whether we support running aapt twice, to regenerate the resources.arsc file such that we can strip out value resources as well. We don't do this yet, for reasons detailed in the ShrinkResources task We have two options: (1) Copy the resource files over to a new destination directory, filtering out removed file resources and rewriting value resource files by stripping out the declarations for removed value resources. We then re-run aapt on this new destination directory. The problem with this approach is that when we re-run aapt it will assign new id's to all the resources, so we have to create dummy placeholders for all the removed resources. (The alternative would be to then run compilation one more time -- regenerating classes.jar, regenerating .dex) -- this would really slow down builds.) A cleaner solution than this is to get aapt to support using a predefined set of id's. It can emit R.txt symbol files now; if we can get it to read R.txt and use those numbers in its assignment, we can solve this cleanly. This request is tracked in https://code.google.com/p/android/issues/detail?id=70869 (2) Just rewrite the .ap_ file directly. It's just a .zip file which contains (a) binary files for bitmaps and XML file resources such as layouts and menus (b) a binary file, resources.arsc, containing all the values. The resources.arsc format is opaque to us. However, MOST of the resource bulk comes from the bitmap and other resource files. So here we don't even need to run aapt a second time; we simply rewrite the .ap_ zip file directly, filtering out res/ files we know to be unused. Approach #2 gives us most of the space savings without the risk of #1 (running aapt a second time introduces the possibility of aapt compilation errors if we haven't been careful enough to insert resource aliases for all necessary items (such as inline @+id declarations), or if we haven't carefully not created aliases for items already defined in other value files as aliases, and perhaps most importantly, introduces risk that aapt will pick a different resource order anyway, which we can only guard against by doing a full compilation over again. Therefore, for now the below code uses #2, but since we can solve #1 with support from aapt), we're preserving all the code to rewrite resource files since that will give additional space savings, particularly for apps with a lot of strings or a lot of translations.
Remove resources (already identified by analyze()). This task will copy all remaining used resources over from the full resource directory to a new reduced resource directory. However, it can't just delete the resources, because it has no way to tell aapt to continue to use the same id's for the resources. When we re-run aapt on the stripped resource directory, it will assign new id's to some of the resources (to fill the gaps) which means the resource id's no longer match the constants compiled into the dex files, and as a result, the app crashes at runtime.
Therefore, it needs to preserve all id's by actually keeping all the resource names. It can still save a lot of space by making these resources tiny; e.g. all strings are set to empty, all styles, arrays and plurals are set to not contain any children, and most importantly, all file based resources like bitmaps and layouts are replaced by simple resource aliases which just point to
"Removes" resources from an .ap_ file by writing it out while filtering out unused resources. This won't touch the values XML data (resources.arsc) but will remove the individual file-based resources, which is where most of the data is anyway (usually in drawable bitmaps)
source
- the .ap_ file created by aaptdest
- a new .ap_ file with unused file-based resources removed