• Startup, the only way to open the APP, the first experience, related to user retention and conversion rate and other core data;

Start the analysis

Start type

  • Android Vitals can monitor cold, hot, and warm startup times.
  • Adb shell am start -w… The command is used to execute the startup and displays the startup time information, which is described in the following startup monitoring section
1. The cold start
  • Applications are started from the beginning, and system processes are created after cold startup
  • Start: Click Event -> IPC -> process. start -> ActivityThread -> bindApplication -> LifeCycle -> ViewRootImpl
  • Three tasks of the system in the cold start phase:
    1. Load and start the application
    2. Displays a blank startup window for the application
    3. Creating an Application Process
  • The application process is responsible for subsequent phases:
    1. Create an Application object
    2. Start main thread
    3. Create a main Activity
    4. Expand view/load layout
    5. Layout of the screen
    6. Perform initial drawing/first frame drawing
  • When the application process finishes drawing for the first time, the system process replaces the currently displayed launch window with the primary Activity. At this point, the user can start using the application.
2. Hot start
  • All the system does is bring the Activity to the foreground,
  • As long as all of the application’s activities remain in memory, the application does not have to perform object initialization, layout bloat, and rendering repeatedly.

However, if some memory is completely wiped in response to a memory trim event (such as onTrimMemory()), the corresponding object needs to be recreated in response to a hot start event;

  • The on-screen behavior of the hot launch display is the same as that of the cold launch scenario: the system process displays a blank screen before the application completes the Activity rendering.
3. The warm start
  • Contains some of the operations that occur during cold startup; At the same time, it is more expensive than hot boot
  • Scenario 1: The user exits the application and then restarts the application (the process may survive and recreate the Activity from scratch by onCreate())
  • Scenario 2: The system evicts the application from memory, and the user restarts it (both the process and the Activity need to be restarted, but the saved instance state bundle passed to onCreate() helps to accomplish this task)
  • The following startup generally refers to the cold start

The boot process

  1. (Desktop) Click response to parse the application
  2. Preview window display (created from the Theme property, if the Theme is set to transparent, you will still see the desktop)
  3. Create an Activity (a series of inflateViews, onMeasures, onLayouts)
  4. (System) Flash screen display
  5. (Application) MainActivity Preparation for creating the interface
  6. Home page/home page display
  7. (Application) Other work (data loading, preloading, initialization of business components)
  8. Window operable

Startup Problem analysis

  • Three problems that users may encounter can be inferred from the startup process
1. No response after clicking the desktop icon:
  • Cause: The preview window is disabled or a transparent background is specified in theme
// Advantages: Avoid blank screen and black screen during app startup // Disadvantages: easy to cause no response when clicking desktop icon // (can be used with three-party library lazy loading, asynchronous initialization, etc., to reduce the initialization time) // Implement the following //0. AppTheme <! -- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <! -- Customize your theme here. --> <item name="colorPrimary">@color/c_ff000000</item> <item name="colorPrimaryDark">@color/c_ff000000</item> <item name="colorAccent">@color/c_ff000000</item> <item Name =" Android :windowActionBar">false</item> <item name=" Android :windowNoTitle">true</item> </style> // set in 1.styles.xml <style name="AppTheme.Launcher"> <item name=" Android :windowBackground">@null</item> <item Name = "android: windowDisablePreview" > true < / item > < / style > / / 1.2 specified transparent background < style name = "AppTheme. The Launcher" > < item name="android:windowBackground">@color/c_00ffffff</item> <item name="android:windowIsTranslucent">true</item> </style> / / 2. To start page < / Activity sets the splash screen page theme Activity android: name = ". Splash. SplashActivity "android: screenOrientation =" portrait" android:theme="@style/AppTheme.Launcher"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category  android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> //3. In this activity.oncreate (), set the AppTheme (before setting the layout ID) // For example, if I'm a separate method from the base class to get the layout ID, then override this method in the startup page with the following configuration:  @Override protected int getContentViewId() { setTheme(R.style.AppTheme_Launcher); return R.layout.activity_splash; }Copy the code
2. The home page is displayed slowly
  • Cause: Complicated startup process, too many initialized components-tripartite libraries
3. Operations cannot be performed after the home page is displayed
  • Reason: same as above

Start the optimization

  • The method is basically the same as caton optimization, but startup is too important and needs to be more careful;

Optimization tools

  • The performance loss of Traceview is too large, and the results obtained are not real.
  • Nanoscope is very realistic, but for now it only supports Nexus 6P and x86 emulators, and cannot be tested on low – and mid-range machines;
  • Simpleperf’s fire chart is not suitable for startup flow analysis;
  • Systrace makes it easy to track the elapsed time of key system calls, but does not support elapsed time analysis of application code;
  • In general, “Systrace + function piling” mentioned in Caton optimization seems to be an ideal scheme (please refer to homework). After getting the panorama of the whole startup process, we can clearly see the operation of the system, application processes and threads during this period of time.

An optimization method

1. Flash screen optimization:
  • Preview flash screen (toutiao), preview window to achieve a flash screen effect, high-end phones experience is very good, but low-end phones will lengthen the total flash screen duration (it is recommended to enable this solution on Android6.0 or above);
// Advantages: avoid no response when clicking desktop ICONS // Disadvantages: lengthen the total screen duration // (can be used with three-party library lazy loading and asynchronous initialization schemes to reduce the initialization duration) //1. <style name="AppTheme.Launcher"> <item name="android:windowBackground">@drawable/bg_splash</item> <item name="android:windowFullscreen">true</item> </style> //2. The bg_splash file is as follows (using layer-list) <? The XML version = "1.0" encoding = "utf-8"? > <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/color_ToolbarLeftItem" /> <item> <bitmap android:antialias="true" android:gravity="center" android:src="@drawable/ic_splash" /> </item> </layer-list> //3. To start page < / Activity sets the splash screen page theme Activity android: name = ". Splash. SplashActivity "android: screenOrientation =" portrait" android:theme="@style/AppTheme.Launcher"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category  android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> //4. In this activity.oncreate (), set the AppTheme (before setting the layout ID) // For example, if I'm a separate method from the base class to get the layout ID, then override this method in the startup page with the following configuration:  @Override protected int getContentViewId() { setTheme(R.style.AppTheme_Launcher); return R.layout.activity_splash; }Copy the code
  1. Merge the Activity (wechat) with the flash screen and the home page, but it violates the principle of single responsibility, and the business logic is complicated;
2. Business sorting
  • Clarify the modules in the startup process, which need, which can be cut, which can be lazy loading (lazy loading to prevent centralization, to avoid the home page visible but users can not operate the situation);
  • Different startup modes are determined according to service scenarios.
  • The low-end machine downgrade, do function trade-off;
  • The positive value of overall retention and transformation brought by startup optimization is greater than the negative impact brought by the cancellation of preloading of a business.
3. Service optimization
  • Focusing on the big and releasing the small, solving the main time-consuming problems, such as optimization of decryption algorithm;
  • Asynchronous threads are preloaded, but overuse can complicate code logic;
  • Pay off technical debt and refactor old code when necessary;
4. Thread optimization
  • Reduce fluctuations caused by CPU scheduling and stabilize application startup time
  1. Control the number of threads, to avoid too many threads competing with each other for CPU resources, using a unified thread pool, according to the machine performance to control the number;
  2. Check locks between threads, especially to prevent the main thread from idling for long periods of time (the main thread is waiting for child thread tasks because of the lock);
// View thread switching data using sched proc/[pid]/sched: nr_volume_switches: Number of active context switches caused by a thread's inability to obtain required resources. The most common type is I/O. Nr_voluntary_switches: Number of passive context switches in which threads are forcibly scheduled by the system leading to context switches, such as a large number of threads preempting the CPU.Copy the code
  • At present, there are many startup frameworks. The Pipeline mechanism is used to specify the business initialization timing according to the business priority. For example, wechat mmkernel and Ali Alpha will establish a dependency relationship for tasks and eventually form a directed acyclic graph.
  • The following is a custom thread pool utility class that can distinguish between multiple types of tasks and can also be used for asynchronous initialization
//- IO intensive tasks: do not consume CPU, the core pool can be large, such as file read and write, network request, etc. // - CPU-intensive tasks: The core pool size is related to the number of CPU cores, such as complex calculations that require a large number of CPU computing units. / / / / execution task is CPU intensive DispatcherExecutor getCPUExecutor (). The execute (YourRunable ()); / / execution task is IO intensive DispatcherExecutor getIOExecutor (). The execute (YourRunable ()); /** * @Author: LiuJinYang * @CreateDate: 2020/12/16 ** Implement basic thread pool for performing multiple types of tasks */ public class DispatcherExecutor {/** * Thread pool for CPU intensive tasks */ private static ThreadPoolExecutor sCPUThreadPoolExecutor; Private static ExecutorService sIOThreadPoolExecutor; /** * sIOThreadPoolExecutor; */ private static final int CPU_COUNT = Runtime.getruntime ().availableProcessors(); */ private static final int CORE_POOL_SIZE = math.max (2, math.min (CPU_COUNT - 1, 5)); Private static final int MAXIMUM_POOL_SIZE = CORE_POOL_SIZE; If the number of threads in the thread pool is greater than corePoolSize or allowCoreThreadTimeOut is set to allowCoreThreadTimeOut, * Threads are checked for keepAliveTime activity and destroyed when timeout occurs. * Otherwise, the thread waits forever for new work. */ private static final int KEEP_ALIVE_SECONDS = 5; Private static final BlockingQueue<Runnable> S_POOL_WORK_QUEUE = new LinkedBlockingQueue<>(); /** * private static final DefaultThreadFactory S_THREAD_FACTORY = new DefaultThreadFactory(); /** * The thread pool is running a time-consuming task when an exception occurs. */ private static final RejectedExecutionHandler S_HANDLER = new RejectedExecutionHandler() {@override public  void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { Executors.newCachedThreadPool().execute(r); }}; /** @return CPU thread pool */ public static ThreadPoolExecutor getCPUExecutor() {return ThreadPoolExecutor; ExecutorService getIOExecutor() {sIOThreadPoolExecutor; ExecutorService getIOExecutor() {ExecutorService getIOExecutor(); /** * implements a DefaultThreadFactory */ private static class DefaultThreadFactory implements ThreadFactory {private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s ! = null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "TaskDispatcherPool-" + POOL_NUMBER.getAndIncrement() + "-Thread-"; } @override public Thread newThread(Runnable r) {Thread t = newThread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); If (t.isdaemon ()) {// non-daemon thread t.setdaemon (false); } // Set thread priority if (t.goetpriority ()! = Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); } return t; } } static { sCPUThreadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, S_POOL_WORK_QUEUE, S_THREAD_FACTORY, S_HANDLER); // If the idle core thread is allowed to timeout, the thread is checked against the keepAliveTime value, and the thread is destroyed once the timeout is set. Otherwise, the thread waits forever for new work. sCPUThreadPoolExecutor.allowCoreThreadTimeOut(true); // CachedThreadPool is used to implement the thread pool of IO intensive tasks. / / it can be allocated most Integer. MAX_VALUE a non-core threads used to perform a task sIOThreadPoolExecutor = Executors. NewCachedThreadPool (S_THREAD_FACTORY); }}Copy the code
5. GC optimization
  • During the startup process, try to minimize the number of GC and avoid causing the main thread to lag for a long time
Python systrace. Py dalvik-b 90960-a com.sample. GC //2. Use debug.startalloccounting to monitor the total GC duration during the startup process. // Total GC duration, in milliseconds debug.getrUntimestat (" art.gc.gtime-counting "); Debug.getRuntimeStat("art.gc.blocking-gc-time"); // If you find that the main thread has a large number of GC synchronous waits, you need to use the Allocation tool for further analysisCopy the code
  • The startup process avoids a lot of string manipulation, serialization and deserialization, and reduces object creation (improve taking or moving to Native implementations);
  • Java object escape is also very easy to cause GC, should ensure the object life cycle as short as possible, destroy on the stack;
6. System call optimization
  • Based on the System Service type of Systrace, you can view the CPU status of the System Server during startup
  • Do not make system calls during startup, such as PackageManagerService and Binder calls
  • The System Server and new processes compete for CPU resources. Running out of memory may trigger the System’s low memory killer mechanism, causing the System to kill and pull up a large number of processes, which affects the foreground process

Start the advanced method

1. IO optimization

  • When the load is too high, I/O performance deteriorates quickly, especially for low-end computers.
  • Network I/OS are not recommended during startup
  • Disk IO to know what files are read during startup, how many bytes, buffer size, how much time, in what thread, etc.
  • Heavy users are the groups that the optimization must cover, such as local cache, database, SP file is very time-consuming
  • For data structure selection, for example, only a few fields in the SP file may be needed at startup, and SharedPreference needs to be stored separately to avoid time-consuming parsing of all SP data.
  • The startup process is suitable for using random read and write data structures, and ArrayMap can be transformed into a data storage mode that supports random read and write and delayed parsing.

2. Rearrange data

  • Linux file I/O process
When a Linux file system reads a file from the disk, it reads the file from the disk in blocks. Generally, the block size is 4KB. This means that at least 4KB of data is read and written to the disk at a time, and 4KB of data is put into the Page Cache. If the file data is already in the page cache the next time it is read, no actual disk I/O will occur and it will be read directly from the page cache, greatly increasing the read speed. For example, reading 1KB of data from a file, because the Buffer was accidentally written as 1 byte, requires a total of 1000 reads. Does the system really read the disk 1000 times? In fact, 1000 reads are just the number of times we do it, not the actual disk I/O. We read 1000 times, but actually only one disk I/O happens, and the rest of the data is in the page cache.Copy the code

Dex files used to the classes and installation package APK in the various resource files are generally relatively small, but read very frequently. We can use the system mechanism to rearrange them in read order, reducing the actual disk I/O;

Class rearrangement
MyClassLoader extends PathClassLoader {public class <? > findClass(String name) {writeToFile(name, "coldstart_classes.txt"); return super.findClass(name); }} // Then adjust the order of the classes in the Dex through the Interdex of ReDex, and finally use the 010 Editor to see the effect of the modification.Copy the code
Resource files are rearranged
  • Facebook has long used resource heat maps to rearrange resource files
  • Alipay described in detail the principle and landing method of resource rearrangement in “Optimize startup Performance of Android terminal through Rearrangement of Installation Package”;
  • The implementation is by modifying the Kernel source code, compiled a special ROM, in order to facilitate resource file statistics, rearrangement to achieve the effect of measurement, process automation
  • If only for statistics, hook method can also be used
    / / Hook, the use of Frida realization method of the Android resource load order resourceImpl. LoadXmlResourceParser. Implementation = function (a, b, c, d) { send('file:'+a) return this.loadXmlResourceParser(a,b,c,d) } resourceImpl.loadDrawableForCookie.implementation=function(a,b,c,d,e){ send("file:"+a) return Enclosing loadDrawableForCookie (a, b, c, d, e)} / / Frida is relatively small, will replace the other more mature Hook behind the frame / / adjust the installation package files arranged 7 zip need to modify the source code implementation supports the incoming file list order, Finally, you can use the 010 Editor to see the effect of the modification.Copy the code
    • Innovation doesn’t necessarily mean creating something that’s never been done before. We transplanted the existing solution to the new platform, and well combined with the characteristics of the platform to implement it, which is a great innovation

3. Class loading

  • In the loading process, there is a verify class step, which needs to verify each instruction of the method. It is a time-consuming operation. You can Hook this step to remove the Verify class step
  • The biggest optimization scenarios are for first and overwrite installations
//Dalvik Globals. H gdvm. classVerifyMode = VERIFY_MODE_NONE; //Dalvik Globals. // Art runtime.cc verify_ = verifier::VerifyMode::kNone; // The ART platform is much more complex, the Hook needs to be compatible with several versions // most Dex has been optimized at the time of installation, // The dalvik_hack-3.0.0.5.jar in Atlas can be removed by using the following method AndroidRuntime.getInstance(); runtime.init(context); runtime.setVerificationEnabled(false); // This hack can greatly reduce the speed of initial startup, at the cost of having a slight impact on subsequent runs. Compatibility issues should also be considered, so it is not recommended to use it on ART platformsCopy the code

4. Black science and technology

Keep alive:
  • Keeping alive reduces Application creation and initialization time and makes a cold start a warm start. But after Target 26, it did get harder and harder to stay alive; (Big companies usually cooperate with manufacturers, such as Wechat Hardcoder solution and OPPO Hyper Boost solution. When the application volume is large enough, manufacturers can be forced to optimize for them.)
Plug-in and hot fix:
  • In fact, most frameworks are designed with a large number of Hook and private API calls, leading to two major disadvantages:
  1. Stability/compatibility: vendor compatibility, installation failure, Dex2OAT failure, etc., non-SDK-Interface call limits introduced by Android P
  2. Performance: Android Runtime has a number of optimizations for each version, and hacks can cause failures
Application reinforcement:
  • It’s a disaster for startup speed, and sometimes there are trade-offs and choices to make
GC: inhibition
  • Refer to alipay client architecture analysis -Android client startup speed optimization of “garbage collection”;
  • Allow the heap to grow until GC suppression is stopped manually or in OOM

5. MultiDex optimization

What happens when you press the compile button?
1. Package resource files and generate R.Java files (using AAPT) 2. Process AIDL files, generate Java code (ignore without AIDL) 3. Compile Java files to generate the corresponding. Class file (Java Compiler) 4.. class files into dex files (dex) 5. Package as apK without signature (using tool ApkBuilder) 6. Use the signature tool to sign the APK (use Jarsigner) 7. Align signed.apk files. They cannot be posted to Google Market without alignment (use zipalign)Copy the code

Unable to execute dex: Method ID not in [0, 0xFFFF]: 65536, so multidex is generally used in our project:

Gradle defaultConfig {... MultiDexEnabled true} implementation 'androidx. Multidex: multidex: 2.0.1' 2. The Application in the initialization @ Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); }Copy the code

However, this multidex process is time-consuming, so can we optimize it for this problem?

Two schemes of MultiDex optimization
1. Child thread install (not recommended)
  • A child thread is opened to execute multidex. install, and then the main page is redirected after loading.
Note that the Activity of the splash page, including other classes referenced in the splash page, must be in the main dex. Otherwise, an error will be reported when loading the classes that are Not in the main dex before multidex. install. This can be configured using Gradle as follows: DefaultConfig {// subcontract, Specifies the obfuscation rules for classes packed to the main dex multiDexEnabled true multiDexKeepProguard file(' multidexkeep.pro ') // MultiDexKeepFile file('maindexlist.txt') // Specify which classes to put in maindex}Copy the code
2. Toutiao program
  1. In the attachBaseContext method of the main process Application, if MultiDex is needed, create a temporary file and start a process (LoadDexActivity) with Loading, Execute the multidex.install logic asynchronously, delete the temporary file and finish yourself.
  2. The attachBaseContext of the main process, Application, enters the while code block to check whether the temporary file is deleted. If it is deleted, it indicates that MultiDex has been executed. Then, the loop breaks out and the normal Application startup process continues.
  3. Note that LoadDexActivity must be configured in the main dex.
  4. Specific implementation of the reference project MultiDexTest

6. Preload optimization

1. Class preloading:
  • Load classes that take a long time to initialize asynchronously ahead of time in your Application
2. Page data preloading:
  • When the home page is free, load data from other pages and save it to memory or database
3. WebView preloading:
  1. It takes time to create a WebView for the first time. You can create a WebView in advance and initialize its kernel in advance.
  2. Use the WebView cache pool. All the places that use WebView are fetched from the cache pool. No cache is created in the cache pool.
  3. The WebView is created by preloading the local HTML and CSS, and then filling the content with JAVASCRIPT scripts.
4. Activity pre-creation: (Toutiao)
  • The Activity object is called when the child thread is pre-new, such as while waiting for an advertisement on a splash page
DispatcherExecutor.getCPUExecutor().execute(new Runnable() {
    @Override
    public void run() {
            long startTime = System.currentTimeMillis();
            MainActivity mainActivity = new MainActivity();
            LjyLogUtil.d( "preNewActivity 耗时: " + (System.currentTimeMillis() - startTime));
    }
});
Copy the code

When an object is first created, the Java virtual machine first checks whether the corresponding Class object has been loaded. If it is not loaded, the JVM looks for the.class file based on the class name and loads its class object. When the same class is new the second time, it does not need to load the class object, but directly instantiates it, and the creation time is shortened.

7. The child process is not started during the startup phase

  • Child processes share CPU resources, causing CPU strain in the primary process

8. The CPU frequency lock

  • Currently, the CPU performance of mobile devices has increased dramatically, but the utilization rate is generally not high. We can stretch the CPU frequency violently during startup to increase the startup speed
  • But it will lead to an increase in power consumption
  • In the Android operating system, CPU information is stored in the /sys/devices/system/ CPU directory. You can write values to specific files in this directory to change status information, such as the CPU frequency.
- CPU operating mode Performance: Indicates the highest performance mode in which the CPU works at the highest frequency even when the system load is very low. Powersave: Power saving mode. In contrast to the Performance mode, the CPU always runs at the lowest frequency. Ondemand: the CPU frequency changes with the system load. Userspace: Can be simply defined as a custom mode in which the frequency can be set.Copy the code

Start monitoring/time detection

logcat

  • The Displayed keyword is Displayed in Android Studio logcat

adb shell

Adb shell am start - W com. Ljy. Publicdemo. Lite/com. Ljy. Publicdemo. Activity. The MainActivity execution results: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.ljy.publicdemo.lite/com.ljy.publicdemo.activity.MainActivity } Status: ok LaunchState: COLD Activity: com.ljy.publicdemo.lite/com.ljy.publicdemo.activity.MainActivity TotalTime: 2065 WaitTime: 2069 Complete //LaunchState: Hot/Cold startup //TotalTime: total Activity startup time. (Main data, including process creation, Application initialization, and Activity initialization to display) //WaitTime: indicates the total time spent by AMS to start the Activity.Copy the code

Laboratory monitoring

  • Through regular automatic screen recording and analysis, it is also suitable for comparative testing of competing products
  • How do you find the start end point
    • 80% draw
    • Image recognition
  • High threshold, suitable for large factories

Online monitoring

Details of startup time calculation:
  • Start end statistical time: the time when the user can actually operate
  • Startup time deduction logic: splash screen, advertising, novice boot time should be deducted
  • Exclusion logic for startup: Broadcast, Server pull-up, and entering the background during startup must be excluded
A measure of how fast a startup is
  • Average startup time (users with poor experience may be averaged)
  • Fast open slow open ratio, such as 2 seconds fast open ratio, 5 seconds slow open ratio
  • Startup time for 90% of users
Distinguish startup types:
  • First install start, overwrite install start, cold start, warm start, hot start
  • The percentage of hot starts can also reflect how active or viable our program is
In addition to monitoring metrics, starting online stack monitoring is more difficult. Facebook uses Profilo tools to monitor the total startup process time and automatically compares different versions in the background to see if there are any new time-consuming functions in the new version.Copy the code

Beware of KPI-oriented startup optimization, which is not a number but a real user experience.

Code point (function piling), the disadvantage is that the code is more intrusive
/** * @author: LiuJinYang * @createdate: 2020/12/14 ** Add dots where time counts are needed in the project, such as * application lifecycle nodes. * Important methods that need to be initialized at startup, such as database initialization, to read some data locally. * Other time-consuming algorithms. */ public class TimeMonitor { private int mMonitorId = -1; */ private HashMap<String, Long> Unique tag = new HashMap<>(); private long mStartTime = 0; public TimeMonitor(int mMonitorId) { LjyLogUtil.d("init TimeMonitor id: " + mMonitorId); this.mMonitorId = mMonitorId; } public int getMonitorId() { return mMonitorId; } public void startMonitor() {// If (mtime.size () > 0) {mtime.clear (); } mStartTime = System.currentTimeMillis(); } /** * Public void recordingTimeTag(String tag) {// If the same tag is saved, clear if (mtime.com)! = null) { mTimeTag.remove(tag); } long time = System.currentTimeMillis() - mStartTime; LjyLogUtil.d(tag + ": " + time); mTimeTag.put(tag, time); } public void end(String tag, boolean writeLog) { recordingTimeTag(tag); end(writeLog); } public void end(Boolean writeLog) {if (writeLog) {// Write to local file}} public HashMap<String, Long> getTimeTags() { return mTimeTag; }}Copy the code
  • Time statistics may need to be handled in multiple modules and classes, so a singleton class is needed to manage the data for each time statistic:
/** * @Author: LiuJinYang * @CreateDate: 2020/12/14 */ public class TimeMonitorManager { private HashMap<Integer, TimeMonitor> mTimeMonitorMap; private TimeMonitorManager() { this.mTimeMonitorMap = new HashMap<>(); } private static class TimeMonitorManagerHolder { private static TimeMonitorManager mTimeMonitorManager = new TimeMonitorManager(); } public static TimeMonitorManager getInstance() { return TimeMonitorManagerHolder.mTimeMonitorManager; } public void resetTimeMonitor(int id) {if (mtimemonitormap.get (id)! = null) { mTimeMonitorMap.remove(id); } getTimeMonitor(id).startMonitor(); } public TimeMonitor getTimeMonitor(int id) {TimeMonitor monitor = mtimemonitormap.get (id); if (monitor == null) { monitor = new TimeMonitor(id); mTimeMonitorMap.put(id, monitor); } return monitor; }}Copy the code
AOP points, such as counting all method consumption in an Application
1. Through the AspectJ
/ / 1. Integrated aspectj/build/root directory. The gradle classpath 'com. Hujiang. Aspectjx: gradle - android - the plugin - aspectjx: 2.0.10' / / app Module build.gradle apply plugin: 'Android-aspectjx' Android {aspectjx {include 'com.ljy.publicdemo'}} //2. @target (elementtype.method) @Retention(retentionPolicy.runtime) public @interface GetTime {String tag() default ""; } @aspect public class AspectHelper {@around ("execution(@getTime * *(..))} )") public void getTime(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature joinPointObject = (MethodSignature) joinPoint.getSignature(); Method method = joinPointObject.getMethod(); boolean flag = method.isAnnotationPresent(GetTime.class); LjyLogUtil.d("flag:"+flag); String tag = null; if (flag) { GetTime getTime = method.getAnnotation(GetTime.class); tag = getTime.tag(); } if (TextUtils.isEmpty(tag)) { Signature signature = joinPoint.getSignature(); tag = signature.toShortString(); } long time = System.currentTimeMillis(); try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } LjyLogUtil.d( tag+" get time: " + (System.currentTimeMillis() - time)); }}Copy the code
2. Through Epic tripartite library
// Epic currently supports Android 5.0 to 11 thumb-2 /ARM64 instruction sets, arm32/x86/x86_64/ MIPS/MIps64 instruction sets are not supported. Implementation 'me. Weishu: Epic :0.11.0' Epic public Static class ActivityMethodHook extends XC_MethodHook{private long startTime; @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); startTime = System.currentTimeMillis(); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); Activity act = (Activity) param.thisObject; String methodName=param.method.getName(); LjyLogUtil.d( act.getLocalClassName()+"."+methodName+" get time: " + (System.currentTimeMillis() - startTime)); }} private void initEpic () {/ / for all the activity's onCreate execution for printing DexposedBridge. HookAllMethods (activity. The class, "onCreate", new ActivityMethodHook()); } / / can also be used to lock the Thread founder DexposedBridge hookAllConstructors (Thread. The class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); Thread thread = (Thread) param.thisObject; LjyLogUtil.i("stack " + Log.getStackTraceString(new Throwable())); }});Copy the code

Homework after class

  • Systrace + function peg
  • How to remove Verify in Dalvik

reference

  • Android development master class – Startup optimization (1) : from the startup process to see the startup speed optimization
  • First pipeline
  • The Practice of Wechat Android Modular Architecture Reconstruction (MMKernel)
  • Alpha Startup Framework
  • Startup Optimization (part 2) : An advanced method for optimizing startup speed
  • ReDex: An Android Bytecode Optimizer
  • 010Editor Template installation and use
  • Optimization analysis of Alipay App construction: Optimize the startup performance of Android terminal by rearranging the installation package
  • Verify Class for Android Hot patch
  • Atlas Dynamic Bundle framework
  • Three years in development, How does OPPO’s Hyper Boost engine speed up systems, games, and applications
  • Alipay client architecture analysis: Android client startup speed optimization “garbage Collection”
  • Android Vitals
  • Android Startup Speed Optimization (Part 1)
  • Epic: A Java Method-granular runtime AOP Hook framework at the virtual machine level
  • Interviewer: Toutiao got off to a fast start. What do you think might have been optimized?
  • Open source | BoostMultiDex: save the Android model Dalvik APP upgrade installation experience
  • Detailed explanation of toutiao App page opening scheme in seconds
  • Android Startup Speed Optimization (Part 2)

My name is Jinyang. If you want to learn more about jinyang, please pay attention to the wechat public number “Jinyang said” to receive my latest articles