Android performance monitoring framework Matrix (7) resource optimization

Android performance monitoring framework Matrix (7) resource optimization

As mentioned in the previous article, in addition to instrumentation, Matrix will also choose whether to perform resource optimization based on user configuration to delete unnecessary resource files.

arsc file format

One of the functions of Matrix resource optimization is to crop resources.arsc. Before analyzing this function, let's briefly understand the file format of arsc.

First introduce a few concepts in the arsc file:

  1. Chunk refers to a data block. The Table, Package, String Block, and Type described below are all Chunk, and they all have information such as file header, type, size, alignment and padding.
  2. Resource Table, one arsc file corresponds to one Resource Table
  3. Package, used to describe a package, a Table corresponds to multiple Packages, and the packageID is the highest eight digits of the resource resID. Generally speaking, the system android is 1 (0x01), and the common one such as com.tencent.mm will be 127( 0x7f), the rest is to start from 2, which can also be specified in aapt.
  4. String Block, a Table has a global string resource pool, a Package has a string resource pool of storage resource type, and a string resource pool of storage resource name
  5. Resource Type, resource type, such as attr, drawable, layout, id, color, anim, etc. One Package corresponds to multiple Types
  6. Config, used to describe the dimensions of resources, such as horizontal and vertical screens, screen density, language, etc., a Type corresponds to a Config
  7. Entry, one Type corresponds to multiple Entry, for example, there are two drawables icon1.png and icon2.png in drawable-mdpi, then there are two entries in the Type of mdpi

The file structure is shown in the figure below:

Delete unused resources

Let's start to analyze how Matrix performs resource optimization.

Matrix will first obtain file information such as apk files, R files, and signature configuration files:

String unsignedApkPath = output.outputFile.getAbsolutePath();
removeUnusedResources(unsignedApkPath,
        project.getBuildDir().getAbsolutePath() + "/intermediates/symbols/${variant.name}/R.txt",
        variant.variantData.variantConfiguration.signingConfig);
 

Next, determine the resource information that needs to be deleted, including the resource name and its ID:

// 
Set<String> ignoreRes = project.extensions.matrix.removeUnusedResources.ignoreResources;
Set<String> unusedResources = project.extensions.matrix.removeUnusedResources.unusedResources;
Iterator<String> iterator = unusedResources.iterator();
String res = null;
while (iterator.hasNext()) {
    res = iterator.next();
    if (ignoreResource(res)) {// 
        iterator.remove();
    }
}

//  R   ID   resourceMap  
Map<String, Integer> resourceMap = new HashMap();
Map<String, Pair<String, Integer>[]> styleableMap = new HashMap();
File resTxtFile = new File(rTxtFile); 
readResourceTxtFile(resTxtFile, resourceMap, styleableMap);//  R  

//  unusedResources   removeResources  
Map<String, Integer> removeResources = new HashMap<>();
for (String resName : unusedResources) {
    if (!ignoreResource(resName)) {
       //  resourceMap  
        removeResources.put(resName, resourceMap.remove(resName));
    }
}
 

Then you can delete the specified resources. The method of deletion is to create a new apk file and ignore the unnecessary resources:

for (ZipEntry zipEntry : zipInputFile.entries()) {
    if (zipEntry.name.startsWith("res/")) {
        String resourceName = entryToResouceName(zipEntry.name);
        if (removeResources.containsKey(resourceName)) {// 
            continue;
        } else {//  apk  
            addZipEntry(zipOutputStream, zipEntry, zipInputFile);
        }
    }
}
 

If the shrinkArsc function is enabled, you also need to modify the arsc file to remove the deleted resource information:

if (shrinkArsc && zipEntry.name.equalsIgnoreCase("resources.arsc") && unusedResources.size() > 0) {
    File srcArscFile = new File(inputFile.getParentFile().getAbsolutePath() + "/resources.arsc");
    File destArscFile = new File(inputFile.getParentFile().getAbsolutePath() + "/resources_shrinked.arsc");

   //  arsc  
    ArscReader reader = new ArscReader(srcArscFile.getAbsolutePath());
    ResTable resTable = reader.readResourceTable();

   //  arsc  
    for (String resName : removeResources.keySet()) { 
        ArscUtil.removeResource(resTable, removeResources.get(resName), resName);
    }

   //  ResTable   arsc  
    ArscWriter writer = new ArscWriter(destArscFile.getAbsolutePath());
    writer.writeResTable(resTable);

   //  arsc   apk  
    addZipEntry(zipOutputStream, zipEntry, destArscFile);
}
 

The way to remove it is to set its Entry to null:

public static void removeResource(ResTable resTable, int resourceId, String resourceName) throws IOException {
    ResPackage resPackage = findResPackage(resTable, getPackageId(resourceId));//  resId   package
    if (resPackage != null) {
        List<ResType> resTypeList = findResType(resPackage, resourceId);
        for (ResType resType : resTypeList) {//  package   ResType 
            int entryId = getResourceEntryId(resourceId);//  entry
            resType.getEntryTable().set(entryId, null);//  null
            resType.getEntryOffsets().set(entryId, ArscConstants.NO_ENTRY_INDEX); 
            resType.refresh();
        }
        resPackage.refresh();
        resTable.refresh();
    }
}
 

Above, the new apk file after removing unnecessary resources has been written.

summary

arsc file structure:

RemoveUnusedResourcesTask execution steps are as follows:

  1. Obtain file information such as apk files, R files, and signature configuration files
  2. Determine the resource information that needs to be deleted according to the unusedResource file and R file provided by the user, including the resource name and its ID
  3. Delete the specified resource, the deletion method is to ignore the resource when writing a new apk file
  4. If the shrinkArsc function is enabled, then modify the arsc file to remove the deleted resource information by setting its Entry to null
  5. Other data is written to the new apk file as it is