Zero, background

Analyze -> Run Inspection by name But there are many drawbacks. Then I saw an idea in StackOverflow based on minifyEnabled and shrinkResources

Get useless code based on the minifyEnabled result

1. MinifyEnabled =true will enable code reduction, so if you know what code minifyEnabled removes, you will know which code is useless

If minifyEnabled is set to True in the description of minifyEnabled in the latest official document, R8 code reduction will be enabled by default. Considering the R8 problem we found in the official document provides a R8 remove or retain the function of the code report developer.android.com/studio/buil… The generated report usage.txt looks something like this:

This file lists the contents of minifyEnabled code that will be reduced: the maximum granularity is class, down to class member variables and methods;

The content removed includes the useless code of the third-party Jar package, as well as the useless code of the project itself

How to generate usage.txt

When minifyEnabled is enabled, R8 is used by default for code reduction, but before R8? And who does the downsizing? Proguard! See this article for more information on the difference

Here’s how they are generated in both cases:

1, do not want to enable R8, generate usage.txt
Set minifyEnabled = true can be compiled, the generated files in the build/outputs/mapping/release (or debug)/usage. TXTCopy the code
2. Start R8 and generate usage.txt
Set minifyEnabled=true 2. Specify the generation path and add -printUsage <output-dir>/usage.txt to proguard-rules.pro 3. Recompile,Copy the code

I’ve compared these two methods for code reduction, and slightly more code is removed after R8 is enabled than proGuard, but overall the difference is not significant. Below: ProGuard on the left, 41,000 lines, R8 on the right, 44,000 lines

3. Based on the content of the Usage file, we filtered according to the package name to get the code of the part that was reduced in the current project. The practice in the third part of the article can be referred to

ShrinkResources = shrinkResources

Is relatively easy to get useless resources, will shrinkResources set to true, the compiled shrinkResources results in the build/outputs/mapping/release (or debug)/resources. TXT. Something like this:

In addition, officials provide a switch to turn on stringent reference checks. After this function is enabled, the number of unusable resources detected greatly increases. However, check whether services are affected

To enable harsh check, add the keep. XML file in the res/raw/ directory

Three, practice

TXT and resources. TXT can be filtered and sorted by task. For reference:

task codeScan(dependsOn: assembleRelease) { ... doLast { if (project.getBuildDir().exists()) { String basePath = project.getBuildDir().path + "/ outputs/mapping/release / / / useless Class File uoUseClassRecode = new File (basePath +" usage. TXT ") of the if (uoUseClassRecode.exists()) { FileReader fr = new FileReader(uoUseClassRecode) BufferedReader reader = new BufferedReader(fr) List<ClassRecorder> classList = new ArrayList<>() ClassRecorder recorder = null String packageName = "${project.android.defaultConfig.applicationId}" if (packageName == null || packageName.size() == 0) { throw new IllegalArgumentException("packageName is empty, Please check if the applicationId property is configured in build.gradle defaultConfig ")} while(reader.ready()){String line = reader.readline () // The new class if (! Line.startswith (" ")) {if (isBusinessCode(recorder, packageName)){// If (isBusinessCode(recorder, packageName)) Classlist.add (recorder)} recorder = new ClassRecorder() recorder. ClassName = line} else { Recorder. ClassMethodList. Add (line)}} reader. The close () fr. Close () / / read over, sort sorting List < ClassRecorder > result = SortByClassName (classList, packageName. Size ()+1) File outPutFile = new File(basePath + "unusedclass.txt ") if (outputfile.exists ()) outputfile.createnewfile () BufferedWriter bw = new BufferedWriter(new FileWriter(outPutFile)) for (ClassRecorder cr : Result) {bw. WriteLine (cr.className)} bw. Close ()} else {throw new IllegalArgumentException(" Compile product file does not exist ")} Boolean CheckResPrefix = true // Unused resource File uoUsedRes = new File(basePath + "resources.txt") if (uouseclassrecode.exists ()) { FileReader fr = new FileReader(uoUsedRes) BufferedReader reader = new BufferedReader(fr) List<String> resList = new ArrayList<>() while(reader.ready()){ String line = reader.readLine() if (line.startsWith("Skipped unused resource")) { String name = line.split(" ")[3] name = name.substring(0, name.size()-1) resList.add(name) } } reader.close() fr.close() File outPutFile = new File(basePath + "unusedRes.txt") if  (outPutFile.exists()) outPutFile.createNewFile() BufferedWriter bw = new BufferedWriter(new FileWriter(outPutFile)) for  (String name : ResList) {bw. WriteLine (name)} bw. Close ()}}} */ static Boolean isBusinessCode(ClassRecorder, String packageName) {if (recorder = = null) return false return recorder. The className. The contains (packageName)} / * * *, */ static List<ClassRecorder> sortByClassName(List<ClassRecorder> List, int defaultStartLength){ List<ClassRecorder> result = new ArrayList<>(list.size()) result.addAll(list) sortByClassName(result, 0, result.size()-1, defaultStartLength) return result } static sortByClassName(List<ClassRecorder> list, int begin, int end, int d){ if(begin >= end){return } int[] count = new int[258] for (int i = 0; i < 256+2; i++) { count[i] = 0; } for(int i = begin; i <= end; Int index = charAt(list.get(I).classname, d) + 2; count[index]+=1; } for(int i = 0; i < count.length-1; i++){ count[i+1] += count[i]; } List<ClassRecorder> result = new ArrayList<>(list.size()); for(int i = begin; i <= end; i++){ int index = charAt(list[i].className ,d) + 1 result[count[index]++] = list.get(i); } for(int i = begin; i <= end; i++){ list[i] = result[i - begin]; } for(int r = 0; r < count.length-2; r++){ sortByClassName(list, begin + count[r], begin + count[r+1]-1, d+1); } } static int charAt(string, d) { if (d < string.size()){ return Character.codePointAt(string, d) } else { return -1; } } class ClassRecorder { String className List<String> classMethodList = new ArrayList<>() }Copy the code

Author of this article: Li Molei, Free Big front End R&D Center