First of all, I recommend you to read “Android development master course” and my previous three exercises:

  • Android Development Master Class after-class exercises (1 ~ 5)

  • Android Development Master class after-class exercises (6-8, 12, 17, 19)

  • Android development master class (22,27, ASM)

Recently, I have brushed the “Android Development Master class” for two times. I have practiced some cases mentioned by the teacher. To those who are studying this column:

1. BadTokenException for Android 7.1.1 Toast

This is a problem mentioned in lesson 2 crash Optimization (Part 2).

Of course, Google fixed this issue in Android 8.0, essentially catching the following exception:

try {
   mWM.addView(mView, mParams);
   trySendAccessibilityEvent();
} catch (WindowManager.BadTokenException e) {
   /* ignore */
}
Copy the code

So if we want to avoid this problem, we need to find the HOOK points. 7.1.1 There is a variable named mTN in the Toast source code, and its type is handler. We only need the agent to capture it.

The simple implementation code is as follows:

private void hook(Toast toast) {
    try {
        Field tn = Toast.class.getField("mTN");
        tn.setAccessible(true);
        Field handler = tn.getType().getField("mHandler");
        handler.setAccessible(true);
		Field callback = handler.getClass().getField("mCallback");
		callback.setAccessible(true);
		/ / replace
        callback.set(handler, new NewCallback((Handler) handler.get(tn.get(toast))));
     } catch(Exception e) { e.printStackTrace(); }}public class NewCallback implements Handler.Callback {

    private final Handler mHandler;

    public NewCallback(final Handler handler) {
        this.mHandler = handler;
    }

    @Override
    public boolean handleMessage(final Message msg) {
        try {
            this.mHandler.handleMessage(msg);
        } catch (final RuntimeException e) {}
        return true; }}Copy the code

This problem is also fixed in Booster of Didi open-source recently. After reading it, I think it is more perfect. If you’re interested, take a look.

Of course, there are still many minor problems with Toast, and there are many open source fixes, which can be found in this article.

2. The Dex file is rearranged

This is a method to optimize startup speed mentioned in Lesson 8 Startup Optimization (part 2). This is mainly done using the Redex tool. If you are not familiar with the use of Redex, please refer to my previous article.

thebenefits:

  • Less IO: Avoid reading in multiple dex files.
  • Reduce memory usage: Avoid preloading unnecessary classes.
  • Less page cache contamination.

Usage:

  • First, connect the mobile phone to obtain the PID of the application process
adb shell ps | grep YOUR_APP_PACKAGE | awk '{print $2}'
Copy the code
  • Get the Heap Dump file
/ / generated adb shell am dumpheap YOUR_PID/data/local/TMP/SOMEDUMP hprof / / get the adb pull/data/local/TMP/SOMEDUMP hprof YOUR_DIR_HERECopy the code
  • Gets the class loading order file
python redex/tools/hprof/dump_classes_from_hprof.py --hprof SOMEDUMP.hprof > list_of_classes.txt
Copy the code

I ran into a few problems here. First, the Python script only supports PYTHon2 and requires PIP, enum, and Enum34 to be installed. Otherwise an error such as PIP Command not found will be displayed.

PIP is a package management tool for Python. In Python2.7, easy_install.py is installed by default, while PIP requires manual installation

Installation method is as follows:

sudo easy_install pip

sudo pip install enum

sudo pip install enum34
Copy the code

Also, this script does not support dump. hprof files generated by devices older than 8.0.

If you want to obtain the class loading sequence of the 8.0 device, you can refer to the solution of copying the ClassLoader in the teacher’s article.

With that done, we have a list_of_classes.txt file with the following format:

com/bumptech/glide/load/resource/bitmap/BitmapResource.class
java/lang/Integer.class
android/graphics/Bitmap.class
libcore/util/NativeAllocationRegistry.class
java/lang/ref/FinalizerReference$Sentinel.class
java/lang/ref/FinalizerReference.class
libcore/util/NativeAllocationRegistry$CleanerThunk.class
sun/misc/Cleaner.class
libcore/util/NativeAllocationRegistry$CleanerRunner.class
android/graphics/Canvas.class
android/graphics/Paint.class
android/graphics/BitmapShader.class
android/graphics/RectF.class
java/util/TreeMap$TreeMapEntry.class
okhttp3/Request$Builder.class
okhttp3/Headers$Builder.class
java/util/ArrayList.class
okhttp3/HttpUrl$Builder.class
java/lang/String.class
java/lang/StringBuffer.class
android/icu/impl/ReplaceableUCharacterIterator.class
android/icu/text/ReplaceableString.class
okhttp3/HttpUrl.class
java/util/Collections$UnmodifiableRandomAccessList.class
okio/Buffer.class
java/lang/StringBuilder.class
java/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1.class
java/util/HashMap$EntryIterator.class
java/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry.class
okhttp3/Request.class
okhttp3/Headers.class
com/bumptech/glide/load/resource/bitmap/LazyBitmapDrawableResource.class
...
Copy the code
  • Finally, execute the optimization command
redex --sign -s test.jks -a key0 -p 111111 -c interdex.config -P proguard-rules.pro -o app_1.apk app.apk
Copy the code

The interdex.config file is configured as follows:

{
  "redex" : {
    "passes" : [
      "InterDexPass"]},"coldstart_classes" : "list_of_classes.txt"
}
Copy the code

We can use the 010 Editor to see our before and after comparison.

The second figure shows the order in our list_of_classes.txt file. (Interdex Pass will ignore that it can’t find the corresponding class in APK)

In fact, Google in Android 8.0 ART optimization also reference a called dexLayout to implement the rearrangement of classes and methods, you can see.

3. Use FileVisitor instead of ListFiles

In Lesson 13 storage Optimization (Middle), the teacher mentioned file traversal. After API 26, it is recommended to use FileVisitor instead of ListFiles, because the time of file traversal depends on the number of files in the folder. “We once had a bug that resulted in tens of thousands of files in a folder. When traversing the folder, the user’s phone directly restarted,” the article said.

General folder deletion methods:

	public static void deleteDir(String path) {
		File dir = new File(path);
		if (dir == null| |! dir.exists() || ! dir.isDirectory()){return;
		}

		for (File file : dir.listFiles()) {
			if (file.isFile()){
				file.delete();
			}else if (file.isDirectory()){
				deleteDir(path);
			}
		}
		dir.delete();
	}
Copy the code

The subfiles are recursively deleted using the dir.listfiles () method. FileVisitor is one of the new Java7 features and is available on Android 8.0. Therefore, the improved method of deleting files is as follows:

	public static void deleteDir(String path) throws IOException {
        File dir = new File(path);
        if (dir == null| |! dir.exists() || ! dir.isDirectory()){return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<Path>(){
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    // Continue traversal
                    return FileVisitResult.CONTINUE;
                }
                /** ** is called when accessing a path fails
                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    // Sometimes null is returned if the iteration of the directory completes without errors
                    if (exc == null) {
                        Files.delete(file);
                        return FileVisitResult.CONTINUE;
                    } else {
                        throwexc; }}}); }else {
            for (File file : dir.listFiles()) {
                if (file.isFile()){
                    file.delete();
                }else if (file.isDirectory()){
                    deleteDir(path);
                }
            }
        }
        dir.delete();
    }
Copy the code

4.PrecomputedText

The first time I heard PrecomputedText was in Lesson 21 UI Optimization (Part 2), which can asynchronously measure and layout. I also wrote a blog, you can click on it.

5.Litho

Litho, like the PrecomputedText I mentioned earlier, put both measure and layout into the background thread, leaving only the draw that must be completed in the main thread, which greatly reduced the load of THE U thread.

Specific principle I will not, you can see Meituan technical team before a share of the article: basic skills | the use of the Litho and principle analysis. I mainly talk about my use process.

I use the Litho RecyclerCollectionComponent realized in my PrecomputedText blog and examples of the same. The code is very simple, I will not post, interested can check out Github. Let’s get straight to the effect:

In terms of frame count, it is slightly inferior to PrecomputedText, but Litho supports more components, not only TextView, but also Image, EditView, etc. It can also achieve the advantages of a flat interface, less memory. It is said to have brought a good performance boost to meituan App, and the official documentation is very detailed, so it is still worth trying.

Well, that’s all for now. If you have inspiration to help, I hope you can like support!