Author: James of Dali Smart Client team

sequence

In order to speed up the application cold start process without excessive business changes, this paper finds the optimization items from the virtual machine class loading process, and compares with the industry solutions, and realizes the semi-automatic analysis function. Classes need to be loaded into the virtual machine and initialized before they can be used or instantiated. The entire process is shown in the following figure: a combination of LoadingClass and InitializingClass.

LoadingClass is designed to load a Class from the Dex to the virtual machine, but does not involve the use or execution process of the Class. InitializingClass is designed to ensure that the class is preceded by an initialization process that is embedded in the use or execution of the class.

Load class

DefineClass loads a Class into the virtual machine via SetupClass, InsertClass, and LoadClass, and returns a mirror:Class object pointer.

  • SetupClass: Sets the class access flag and ClassLoader.
  • InsertClass: inserts the class into the ClassTable of the corresponding ClassLoader for look-up purposes.
  • LoadClass: loads the properties and methods of the class into the class.

Class initialization

Properties or methods of a class must be initialized by the class before they can be used.

  • InitializeClass: Validates classes, initializes superclasses, interface methods, and static properties.
  • VerifyClass: validates the validity of a class, which is analyzed in detail in the next section.

Check the class

VerifyClass checks classes using either the VerifyClassUsingOatFile or PerformClassVerification methods. PerformClassVerification contains the VerifyClass Tag in Systrace, as shown in the figure below:

  • VerifyClassUsingOatFile: Verify the Class through the Class status bit in the Oat file. When the status bit equals to kStatusVerified, the verification process stops here and returns directly and quickly. Otherwise, enter the time-consuming PerformClassVerification process.

  • PerformClassVerification: Checks direct and virtual methods in primary classes.

  • ComputeWidthsAndCountOps: Determines whether the PC value is equal to the dalvik instruction number.

  • ScanTryCatchBlocks: checks the validity of the start address, end address, and start operator of the Try statement. Check the validity of the start operator of the handler statement in the catch.

  • Check various Dalvik instructions and insert GC checkpoints into parenthesis, switch and throw instructions.

  • VerifyCodeFlow: Checks the validity of registers and parameters of each Dalvik instruction.

Found in advance

From the above analysis, it can be seen that the verification should go through the VerifyClassUsingOatFile process as far as possible, that is, the verification succeeds through Oat file status bits. What is the status bit of the class in the Oat file and why the status bit does not equal kStatusVerified is the breaking point.

Use oatdump command to dump the corresponding odex file to view the status bit of the class. The operation mode is as follows:

The VLOG is not printed by default and needs to be enabled dynamically. You can enable the VLOG by using the following methods: Art :: glogverbosity. class_linker = true and open, because this project needs to see the printing situation of dex2OAT and other processes. I compile and generate so in the system source code, and then inject so into Zygote through ptrace. This method requires the root device. If you only need to check the process, it should not be so troublesome. The specific method has not been explored, but the idea should be the same. For example, the problem I encountered was that classes in the AppCompat package could not be verified.

The solution

Verify_ Settings in the Runtime object into a verifier: : VerifyMode: : kNone.

  • You need to iterate through the first address of the Runtime object to find the verify_ attribute. Changing the vendor may cause compatibility problems.
  • The lack of VerifyClass process may result in an illegal instruction problem.
  • Modifying the median value verify_ of Zygote will cause cow memory consumption.
  • Will be more EnsureSkipAccessChecksMethods step processing logic, the class of each function flag, modified the logic not to deal with a single class, so, each class of each function of flag will be meaningless, as shown in the figure below:

  • Face the problem itself and correct the source code through the output information of VLOG. In this case, it is because the AppCompat library uses statements that are not supported by the system, as shown in the figure below:

  • This App running environment is on the 8.1 (API27), TextView setFirstBaselineToTopHeight without method, therefore, illegal cause the failure of such check for instructions. Note that build.version.sdk_int is not compile-optimized, it is final, but its value is equal to systemProperties.getint (“ro.build.version.sdk”, 0), so it must be determined at runtime. I tried the following methods:

    • Set the value of build.version.sdk_int in the system source SDK to 27 to compile the new SDK, and then overwrite the source Android.jar with this SDK. It is hoped that the judgment logic of build.version.sdk_int >= 28 in AppCompat will be optimized during compilation, but actually AAR will not participate in the compilation of SDK, and this can only optimize the logic of the project itself.
    • Download the appCompat source code, remove the illegal instructions, and recompile it into aar.
    • Build the support v7 package directly from the android8.1 source code.
    • In both cases, you can customize your OWN AAR and even tailor resources, but you run into a critical problem: the newly generated AAR cannot be published to Maven, so you need to push the business to change the package name. Another problem is if a third party AAR in the project relies on AppCompat. Therefore, by making ASM plug-in, the value of build.version.sdk_int was set to fixed 27. The problem was solved, and the APK size in this project was reduced by 22K.

    If the application needs to be compatible with multiple VERSIONS of ROMs, you can also use the App Bundle to deliver the “most appropriate” App based on the different VERSIONS of ROMs.

platform

In order to reduce the difficulty of implementation of the scheme, the scheme has been platformalized. As long as THE APK is dragged into the web page, the reason why the class verification fails can be seen.