Record the entire process of boot memory analysis

Author: zzy’s study notes

Record the entire process of boot memory analysis, introduce the use of common memory analysis tools and command lines in as much detail as possible, and discuss the practical experience of boot memory analysis based on specific problems. Through this article, I will introduce the basic usage of common testing and analysis tools for boot memory, and how to obtain the next analysis ideas through the captured memory data, including BootRemainMemory, MTN, Profiler, Mat, Meminfo, Smaps, Showmap.

Article overview:

Problem background:

Test colleagues reported that there was a large increase in the boot memory. At the same time, by comparing the versions that packaged and removed the business X module, we found that the problem may lie in business X, so we need to analyze the specific reasons.

illustrate:

This question analyzed is just an introduction. The purpose is to learn and master the common tools and ideas of memory analysis by analyzing this question, so the content will be a bit lengthy. If there are any inaccuracies, please feel free to discuss them.

1. Discover problems

1. Use BootRemainMemory to test boot memory

1.1 Test method

Conduct multiple sets of tests according to the test method.

There are several points to note when using BootRemainMemory:

  • There is a difference in the boot memory between manually overwriting and installing applications and directly built-in system version. According to colleagues, this is because the two methods have different effects on the memory partition. Therefore, if we do not have the conditions to refresh the built-in version every time, we can only overwrite the installation. , then just focus on the incremental part of the comparison.

  • If the setting interval is too short, the test data will fluctuate too much, so it is best to test at the default time for this step, which is also more time-consuming.

1.2 Test results

With business X mean 46.5

Without business X mean value is 43.4

Result: The boot memory does increase by about 3MB.

My experience here is that before testing any performance issues, you must first confirm these items to avoid wasting time: testing tools + testing methods + test packages + running environment of the test packages

2. Check whether related components with self-starting function are introduced

I first suspected whether the memory was added due to the addition of components with self-starting functions, so I chose to check the content provider and static broadcast first.

2.1 ContentProvider

Through previous articles analyzing the startup characteristics of ContentProvider + previous Provider optimization experience, we know that the startup timing of Provider is to follow the application to start automatically, and the onCreate method of Provider is executed earlier than the onCreate of Application, which can easily lead to the increase of startup memory.

Therefore, the first suspicion is that business X has a self-starting Provider that introduces startup logic, causing this part of memory to grow, so we need to find the incremental ContentProvider.

2.1.1 Method 1

By parsing the merged AndroidManifest file in the apk, searching for components declared by the provider tag, we found that a ContentProvider was introduced by the commercial advertising sdk.

2.1.2 Method 2

In addition to viewing the Manifest file, you can also view declared components through "dumpsys package + package name".

2.1.3 Method 3

Use MAT's OQL query statement to query all Provider instances. This method is more accurate than Profiler, because if you simply use Profiler's search function, you cannot identify instances whose names do not include Provider.

SELECT * FROM INSTANCEOF android.content.ContentProvider 

2.1.3 Investigation conclusion

Combined with Profiler, we can see that there is indeed a ProcessProvider loaded during the boot phase.

It is further necessary to investigate the specific role of the Provider and the scope of its impact on the boot memory. Further consult advertising colleagues to check whether there is abnormal memory usage in the onCreate life cycle of this ContentProvider.

The confirmed conclusion is: the current initialization basically does not involve business memory usage.

2.2 BroadcastReceiver

Static broadcasts will also be registered immediately after booting, so it is also necessary to check whether any static advertisements are initialized after booting and the relevant logic is initialized.

If we should not respond to the received function without the user's consent to declare permissions, this will also indirectly cause memory growth.

Examples of startup memory growth caused by static broadcast have also appeared in our project before.

Therefore, we also used the above troubleshooting method for Provider and found that there was no corresponding Receiver, ruling out the possibility of this reason.

3. Use Profiler to identify suspected package names and classes

When there is a certain target of suspicion, it will be more efficient to directly use AS's Profiler to search. Therefore, I searched for the classes of business X that I could think of, and only found the necessary classes related to DI injection, and no related business loading.

3.1 Profiler basics

Memory Profiler is a component in Android Profiler that helps identify memory leaks and memory thrashing that can cause your app to stutter, freeze, or even crash. It displays a real-time graph of application memory usage, can capture heap dumps, force garbage collection, and track memory allocations.

Here we only introduce some tips on how to use Profiler to view memory heap dump files. Use Profiler to grab memory files or obtain hprof files. After opening them with Profiler, you will see the following interface.

4. Use Mat to compare Heap changes

There are still certain limitations in using Profiler analysis. I can only check the known and suspected related classes one by one. I am not sure about the introduction of unknown classes, so I further grabbed two hprofs and used MAT for comparative analysis, trying to find out Specific reasons for the increase.

Scenarios suitable for using mat analysis include: needing to compare memory before and after a certain behavior, needing to compare two different versions of memory, etc.

4.1 Mat basics and analysis skills

4.1.1 Histogram

The main function of Histogram in MAT is to check the number of instances. It is generally used to check the number of instances of classes created by yourself. By checking the number of Objects and combining it with the code, you can find classes with memory leaks. Histogram can also Group objects to make it easier to view object information in your own Package

4.1.2 Dominator Tree

You can easily find out the objects that occupy the most memory and sort them according to Percentage.

The difference between Dominator Tree and Histogram is that the perspective of the station is different. The Histogram is viewed from the perspective of the class, while the Dominator Tree is viewed from the perspective of the object instance of the station. The Dominator Tree can more easily see its reference relationship.

4.1.3 List objects -> with incoming references/with outcoming references

View external object references held by this object/View which external objects this object is referenced by

4.1.4 Path To GC Roots -> exclude all phantim/weak/soft etc. references:

Check the GC Root of this object. It does not include virtual, weak references, and soft references. The rest are strong references, which are usually used to check the root reference relationship of memory leaks. There are several other modes that I will not explain one by one.

4.1.5 OQL statement

SELECT * FROM com.oplus.assistantscreen.window.AssistantWindowBlurSlideView //Query a specific class

SELECT * FROM INSTANCEOF android.content.ContentProvider // Query all instances of Provider and its subclasses

SELECT * FROM INSTANCEOF java.lang.Exception exceptions// Query all exception objects and instances of their subclasses

SELECT * FROM java.lang.String s WHERE s.count >= 100 //Query for strings whose length exceeds 100

SELECT * FROM INSTANCEOF java.util.HashMap s WHERE s.size>10//Query HashMap whose length exceeds 10

4.2 hprof command line capture method

There are certain conditions for capturing hprof. If the whole machine is a root version, it can be captured regardless of whether it is a release or debug version. If it is a non-root version, it needs to be a debug process to capture it.

The hprof file can be dumped in the code, you can use Android Studio to dump, or you can use other third-party tools to dump.

Here I introduce another way to use command line dump. First query the process number - then use dumpheap to capture - and finally export the file.

D:\Users\80343288>adb shell
OP5267:/ # ps -ef|grep assis
u0_a237       4907   833 2 14:27:29 ?     00:00:00 com.xx.xx
u0_a237       5530   833 12 14:27:33 ?    00:00:03 com.xx.xx:xx
root          8663  7044 15 14:27:55 pts/1 00:00:00 grep assis
OP5267:/ # am dumpheap -g 4907 /data/local/tmp/heapdump_noinfo.hprof
File: /data/local/tmp/heapdump_noinfo.hprof
Waiting for dump to finish...
OP5267:/ # exit
D:\Users\80343288>adb pull /data/local/tmp/heapdump_noinfo.hprof
/data/local/tmp/heapdump_noinfo.hprof: 1 file pulled, 0 skipped. 30.5 MB/s (36076684 bytes in 1.130s)

Generally, before importing hprof files captured using AS or the command line into Mat, you need to use the hprof-conv tool to convert the format.

First enter the hprof-conv tool directory that comes with the sdk (D:\Software\Android Studio Others\win-sdk\platform-tools directory)

Execute the following command:

hprof-conv -z memory-20200625T145636.hprof mat.hprof

4.3 Comparison using Mat

Use mat to import the two hprof files that need to be compared, and then select the comparison

Pay special attention not to use the release version of the hprof file if you want to compare, because the class names after each obfuscation are inconsistent, which will lead to low readability of the comparison.

4.4 Troubleshooting results

From the comparison results here, we can see that there is no obvious abnormality in the number of object allocations. Since hprof can only observe the allocation of Java heap memory, the increase should come from other sources, and more complete memory files need to be captured for analysis.

Note: Although we have not reached a conclusion on this issue here, under normal circumstances, if there is a memory leak or large memory allocation exception, it will be exposed intuitively by comparing the two memory files and sorting them. This is very Effective method.

5. Grab meminfo to check the overall memory status

5.1 meminfo basics and analysis skills

You can use meminfo to view the overall memory usage of a specified process or package name. Generally, after viewing this file, you can determine which large block of memory has the problem. Moreover, this memory information is relatively low-cost to obtain, and it is the most commonly used way for us to analyze memory. .

The data source of meminfo is classified and summarized by parsing smaps and other information. The data unit is KB. For detailed principles, please see the reference document later.

5.1.1 Basic interpretation of meminfo

5.1.2 Common analysis ideas

The analysis idea is generally to grab two copies of meminfo for comparison. Based on the comparison results, follow the following path to further analyze which part of the problem is it.

  1. If part of the Dalvik memory becomes larger, you need to check the hprof file.

  2. If the Native memory becomes larger, you need to analyze it according to the Native Debug documentation and the hprof file. The increase in the Native memory of most apps is caused by calls to the Java layer.

  3. If Graphics increases, check the specific GL mtrack/EGL mtrack

    1. Need to see the results of gfxinfo

    2. It is necessary to compare the resolution of the two machines and the usage of SurfaceView, TextureView, Webview, etc. of the App

    3. Need to check the usage of App hardware acceleration

  4. If so/jar/apk/ttf becomes larger, further obtain smaps to check the number of so/jar/apk/ttf, and compare to see which part has become larger, or it is caused by which so/jar/apk/ttf is added.

  5. If dex/oat/art becomes larger, you need to compare the running status of the two apps and whether the application version numbers are consistent. Since this part is closely related to the Android runtime, you need to use the user version for testing.

5.2 Grab meminfo

This command line does not require rooting of the phone, and does not require a debug version of the application.

Note: Generally, you can dump several times and the last one shall prevail, because each execution will trigger a forced GC by default.

D:\Users\80343288\mem_tmp>adb shell dumpsys meminfo com.xx.xx> meminfo_noinfo.txt
5.2.1 Introduction to dumpsys command

The dumpsys command is very useful. In addition to "dumpsys meminfo + package name" to capture memory, you can also "dumpsys package + package name" to view package information (previously I saw other colleagues use this command to check whether the application has a system flag to determine whether (ANR caused by being frozen because there is no system logo), "dumpsys gfxinfo + package name" to view the rendering information and then check the lag situation.

adb shell dumpsys [options]
■ meminfo 内存
■ cpuinfo CPU
■ gfxinfo 帧率
■ display 显示
■ power 电源
■ battery 电池
■ batterystats 电池状态
■ location 位置
■ alarm 闹钟
■ account accounts
■ activity 显示所有的activities的信息
■ window 显示键盘,窗口和它们的关系
■ wifi 显示wifi信息

5.3 Troubleshooting results

After checking and comparing the two meminfo, the conclusion is that the growth is mainly caused by dex mmap.

You can check the general situation of the memory through meminfo. For further analysis, you need to grab the smaps file.

6. Grab smaps to view memory details

When we obtain memory through dumpsys meminfo, we find that a certain item of memory data is abnormal. If we want to find out which files the data is generated from, we can read smaps to check in detail or perform incremental comparisons. Smaps aggregates the collected data. You can clearly see the memory occupied by which dex, so, ttf, and oat. This part of the information is not available in adb shell dumpsys meminfo.

6.1 Basics of smaps

The memory statistics measured using smaps are consistent with those using adb shell dumpsys meminfo. The data in the three columns of Pss, Shared Dirty, and Private Dirty under the dumpsys meminfo command are generated by reading the smaps file.

The meaning of the basic information is as follows:

6.2 Grab smaps

Capturing smaps files must require root permissions. This is also an advantage of mobile phone manufacturers. The grabbing command is as follows:

adb shell cat /proc/$pid/smaps  > smaps.txt,//需要root权限,无需一定要debug版本应用

Since testing the boot memory requires clearing data, we will introduce a convenient command to clear data (higher versions require root)

adb shell pm clear com.xx.xx

smaps records the original information of the memory mapping of this process, but smap is not very friendly when viewed directly. Generally, a script is used to combine the results of smaps and meminfo.

6.3 Parsing smaps

First, let’s introduce how to use the smaps_parser.py script for parsing.

Analysis script:

Parse command:

python D:\Users\80343288\Downloads\smaps_parser(1).py -f D:\Users\80343288\smaps.txt >smaps_parsed.txt

Parsed file:

6.4 Check and compare smaps results

By grabbing smaps, parsing and comparing the two versions, we know that the increase in dex mmap is due to the increase in the base.vdex file.

The dex mmap files differed by about 6MB, so I went to the corresponding directory to view the original files for confirmation, and found that the two original vdex files did differ by about 10MB, as shown in the figure:

7.showmap

In fact, the problem has been located through smaps. Here is an additional introduction to showmap.

It is more difficult to analyze smaps before they can be parsed, so you can also directly grab showmap to check the memory situation. Showmap parses the information of smaps. Here you can see the memory occupied by each open file in the process, but compared with The parsed smaps are still not as intuitive as smaps, and there is no classification and summary.

The fetch command is as follows:

adb shell showmap –t $pid > showmap.txt //Requires root permissions, no need to debug version application

2. Analyze the problem

In summary, we know through a series of tools that the increase in boot memory is due to the growth of the base.vdex file. Then we first find out what vdex is and what is its relationship with dex, odex, oat, and art files?

1.dex related concepts

1.1 dex

dex files are files that can be recognized and executed by the Dalvik virtual machine.

The .class file executed by the JVM can be converted into dex through the dx.bat tool. Dalvik will execute the dalvik bytecode in the .dex file, but generally Dalvik executes the dex-optimized file (i.e., the odex file).

1.2 vdex(Verified Dex)

The vdex file is a new format package for Android O (Android 8.0). Its purpose is to reduce the dex2oat time.

In order to avoid unnecessary verification of the legality of the Dex file, for example, when performing dex2oat during the first installation, the legality of each section of the Dex file will be verified. The compiler filter used at this time does not use full compilation in order to take care of the installation speed and other aspects. When the app disk starts and runs for a period of time, enough jit hot method information is collected. Android will re-execute dex2oat in the background to compile the hot method into machine code. At this time, there is no need to repeat the process of verifying the Dex file.

1. After the system is OTA, for the apps installed in the data partition, because their apk has not changed, when the system is turned on for the first time, if there is a vdex file for this part of the app, it can be skipped directly when executing dexopt. verify process, enter the compile dex process, thereby speeding up the first boot speed;

2. When the jit profile information of the app changes, background dexopt will re-do dex2oat in the background. Because of vdex, you can skip it directly at this time.

1.3 odex(Optimised Dex)

odex is the abbreviation of OptimizedDEX, which represents an optimized dex file and is stored in the /data/dalvik-cache directory.

Since the apk files of Android programs are in zip compressed package format, the Dalvik virtual machine needs to read the classes.dex file from the apk every time it loads them, which will consume a lot of CPU time. The dex file optimized by odex method already contains Loading the list of dependent library files necessary for dex, the Dalvik virtual machine only needs to detect and load the required dependent libraries to execute the corresponding dex file, which greatly shortens the time required to read the dex file.

For the Dalvik virtual machine, odex stores the optimized bytecode after JIT (Optimized Dalvik EXcutable file)

For ART, odex stores local machine code compiled by AOT (Ahead Of Time) (ie: oat file, a private ELF file format)

Before Android N, before the Dalvik virtual machine executed the program dex file, the system would optimize the dex file, generate the executable file odex, save it to the data/dalvik-cache directory, and finally delete the dex file in the apk file.

After Android O, odex extracts some modules from the vdex file to generate a new executable binary code file. After odex is extracted from vdex, the size of vdex is reduced.

Specific process:

1. The first boot will be generated under /system/app//oat/

2. While the system is running, the virtual machine copies it from "/system/app" to "/data/davilk-cache/"

3.odex + vdex = all source code of apk (vdex is not a file independent of odex, odex + vdex represents an apk)

1.4 oat

The ART virtual machine runs an oat file. The oat file is an Android private ELF file format. The oat file contains local machine instructions translated from the dex file, and also contains the original dex file content (as shown in the figure below). Therefore oat files are larger than odex files. During the APK installation process, an OAT file will be generated through the dex2oat tool (the file suffix is ​​still odex).

For apk, the oat file is actually the packaging of the odex file, that is, oat=odex. For some jar packages in some frameworks, the corresponding oat suffix file will be generated, such as system@framework@boot-telephony- common.oat

Note: For Android5.0 and later versions, the suffix of the oat file is still odex, but it is no longer the file format before android5.0, but the local machine code encapsulated in the ELF format. It can be thought that oat adds a layer of shell to dex, and dex can be extracted from oat

1.5 art

The purpose is to speed up application startup.

The art file is a file generated after the virtual machine executes the odex file and records the address information of the common functions started by the virtual machine when executing the Apk (recording the function address information is convenient for addressing). It is usually saved in the data/dalvik-cache/ directory. The relevant address record of the jar package

2.dex execution process

3.dex mmap

The role of dex mmap in Android applications is to map classes.dex files. The dalvik virtual machine needs to load class information, string constants, etc. from the dex file;

It is also necessary to read the function code (dvm bytecode) directly from mmap memory for execution when calling the function, so this part of memory is essential for program running.

A question arises here. Through the previous analysis, we can determine that the hprof file does not instantiate related classes that cause memory growth. Is dex mmap loaded all at once, not on demand?

3.1 dex is not completely loaded on demand

Taking a sample application as an example, we can see in MAT that the application loads about 1,500 class types, and the dex file has a total of 10,635 class types.

After using the dex mmap dynamic statistics function, we found that although only 1500 classes were loaded, the dex memory is usually as high as 4-6M, which is almost half the size of the dex file. As can be seen from the above data, a large part of the dex memory space is wasted, and the actual data and code used are not that much. Why is this?

This is because the dex files are arranged in alphabetical order when generated. Due to 4K page loading, a lot of adjacent data that will not be used will be loaded during actual operation. For example, if class A1 is used in the code, the virtual machine needs to load the page containing class A1 data. But since the data of A1 is only 1K, in the loaded 4K page, there will also be categories A2A3A4, occupying a total of 4K memory. Assuming that after using the A1 class in our code, we will also use the B1C1D1 class, then many additional dex files will be loaded.

3.2 dex optimization

Then if the A1B1C1D1 classes can be put together in the dex file, the virtual machine only needs to load one 4K page, which not only reduces memory usage, but also benefits the startup speed of the program.

Therefore, the idea of ​​​​optimizing dex is to adjust the order of the data in the Dex file, arrange the usable data closely together, or directly subtract the dex. There are many specific ideas, which we will summarize later .

In addition, we also gained an experience: when optimizing memory, not only heap memory, but also many other types of memory can be analyzed and optimized, such as the memory brought by dex here.

3. Summary of startup memory optimization ideas

Through the previous analysis, the cause of the boot memory increase problem mentioned at the beginning of our article is very clear: it is mainly caused by the increase in business X static code and dependent libraries .

The most direct solution to the problem of increasing dex is definitely to subtract the code. This is actually the same as the idea of ​​apk size optimization. However, in daily development, as the demand increases, the amount of code will inevitably increase. Sometimes It may not be possible to further reduce the code. In fact, we have many other ideas to save the country through curves. Here I will make an expansion and classify and summarize the collected methods.

1.Dex layout optimization

Add all code to be executed during startup to the main classes.dex file, and remove all non-startup code from the main classes.dex file. The user provides the sequence of classes to be loaded when the program starts as a configuration file. Adjust in this order. The order of classes in dex can effectively improve cold start speed and optimize dex mmap memory.

There are two ways to rearrange the Dex layout. You can use the official plug-in to configure it or use Facebook's redex plug-in.

However, one is limited by the environment, and the other is limited by the gradle plug-in version requirements. It is difficult to implement, but if it can be successfully implemented, the effect will be obvious.

2. Lazy loading on demand

The purpose of startup memory optimization is to occupy less memory before the user agrees to declare permissions or before the business is officially used. Therefore, our lazy loading condition is that only when the user agrees to the permission, the required content will be loaded; or the corresponding business class will be initialized only when the corresponding function is actually used, avoiding all unnecessary early initialization.

2.1 Provider optimization

–The same Provider that applies IPC function can merge multiple providers into one

– Avoid doing things in the Provider’s lifecycle functions

– Manually collect business logic deferred initialization with the help of Provider

– Just use StartUp instead as the initialization entry

– Use manual calls instead of triggering initialization through Provider (such as WorkManager, StartUp, Lifecycle, LeakCanary)

2.2 Check whether startup is loaded on demand

The startup framework is started at boot by default. You need to remove the default Provider and replace it with manual delayed initialization.

 <provider
	android:name="androidx.startup.InitializationProvider"
	android:authorities="${applicationId}.androidx-startup"
	tools:node="remove" />

There are three ways to initialize WorkManager: 1. Initialization by declaring the default WorkManagerInitializer. The disadvantage is that it will be loaded even if it is not used. 2. Manual initialization by explicitly calling WorkManager.initialize. The disadvantage is that WorkManager.getInstance is called without initialization and then used. Will crash 3 is Application implements Configuration.Provider interface delayed initialization on demand

Special attention should be paid to preventing the WorkManager bound to the startup from triggering reading the WorkDatabase and resuming the execution of scheduled tasks when the startup is started, resulting in an increase in the startup memory.

You can see that our project currently uses the default startup method and does not configure remove for the InitializationProvider, which has startup memory risks and can be optimized.

2.3 Avoid early loading of unnecessary third-party libraries

For example, RxJava delayed initialization can use a custom thread pool instead of thread distribution.

2.4 Use static broadcast with caution

Static broadcast will start receiving external broadcasts when it is turned on, which is likely to cause unnecessary memory usage. You can remove the static broadcast and consider replacing it with dynamic broadcast.

2.5 Lazy initialization of any non-essential classes and properties

Trigger the initialization of the corresponding business class after the user enters and agrees to the permissions or when entering the corresponding page and actually wants to use the class.

Use by Lazy for attributes

2.6 Koin optimization

2.6.1 Registration on demand

Added on-demand lazy registration, which is used to register after agreeing to the permission statement.

For example, in our current project, all classes are registered when the Koin process starts. There is room for optimization. You can add an annotation type to control the registration timing to avoid registration at startup. The current memory usage is as follows

2.6.2 Lazy injection

Use the inject method to obtain the injected object and avoid using Koin.get to obtain it.

//非懒加载,get()这里的功能是直接检索实例(非延迟)
 val str : String = getKoin().get()
//懒加载,在用到的时候才注入
 val str2:String by inject()
 
 实现原理上injec只是对get进行了Lazy封装而已,时机也是调用get方法

3. Code reduction

3.0 Coding standard optimization

Use tables for unified management of shared capabilities to facilitate querying and prevent different developers from adding duplicate codes with the same capabilities.

3.1 PB downgraded to JSON

Due to the characteristics of PB, a very simple piece of code will generate a huge amount of formatting code, and this huge amount of code must not be confused.

On the one hand, PB exists to reduce transmission volume and thus reduce network load, but in fact some of them are very low-frequency and do not need to occupy the scarce memory of the client. There is also some room for optimization here.

On the other hand, it is to check whether there are PB objects that can be subtracted. For example, the general interface defined by the server has 10 fields, but we only need to use 5, then we can only define these 5 fields.

For example, this field also uses pb, which causes a very simple piece of code to generate a huge amount of formatting code.

3.2 Use the scanning tool sonar to check duplicate codes for optimization

Use tools to check for duplicate code and manually optimize it

4. Code obfuscation

4.1 Adding confusion

Obfuscation reduces the size of your app by shortening the names of your app's classes, methods, and fields

Obfuscation can detect and remove unused classes, fields, methods and properties

4.2 Obfuscated grouping based on package name

The data in the Dex file is basically arranged in alphabetical order by class name, so that classes with the same package name will be arranged together. However, in actual program execution, classes under the same package are not all called together, but interact with classes under many other packages. However, mmap loads the entire page, and there may be a lot of useless data. In order to reduce this situation, we should try our best to arrange the data content used together when generating the file. In the APK compilation process, the Proguard obfuscation tool can modify the class name. According to the logic of program operation, classes that will call each other can be changed to the same package name, so that their data can be arranged. Together , by obfuscating the order of dex files, reducing the open file page size due to the 4k limit

4.3 Reduce confusion granularity

·For example, only exempt custom Views that need to be referenced by xml files instead of exempting all custom Views

·For example, only exempt the CREATOR member variable in the Parceable implementation class, not the entire class

5. Class library tailoring

5.1 Integrate libraries that use lite libraries and other libraries with the same functions but smaller memory footprint

5.2 Keep only one library with duplicate functionality

For example, the video player is optimized to use only one, and the picture library only integrates one

Android study notes

Android performance optimization article: https://qr18.cn/FVlo89
Android Framework underlying principles article: https://qr18.cn/AQpN4J
Android vehicle article: https://qr18.cn/F05ZCM
Android reverse security study notes: https://qr18.cn/CQ5TcL
Android audio and video article: https://qr18.cn/Ei3VPD
Jetpack family bucket article (including Compose): https://qr18.cn/A0gajp
OkHttp source code analysis notes: https://qr18.cn/Cw0pBD
Kotlin article: https://qr18.cn/CdjtAF
Gradle article: https://qr18.cn/DzrmMB
Flutter article: https://qr18.cn/DIvKma
Eight knowledge bodies of Android: https://qr18.cn/CyxarU
Android core notes: https://qr21.cn/CaZQLo
Android interview questions from previous years: https://qr18.cn/CKV8OZ
The latest Android interview questions in 2023: https://qr18.cn/CgxrRy
Android vehicle development position interview exercises: https://qr18.cn/FTlyCJ
Audio and video interview questions:https://qr18.cn/AcV6Ap

Guess you like

Origin blog.csdn.net/maniuT/article/details/132812340