APP performance is all about stability, smoothness and the like. What’s the key when it comes to improving performance? Enthusiasm? Capacity? Specification? In my opinion, it is a tool. If you use a good performance analysis tool, you can improve performance by more than half. It is like “I can’t beat Xiao Wang in arithmetic, but I found an electronic calculator”. Take a look at what the overall performance optimization process should look like and what performance tools can bring to the table, using cold start speed as an example.
Definition of cold start with optimizable points
How to measure the current performance indicators, personal feelings, performance measurement can be divided into three steps: index system -> index collection -> performance baseline and quality rating, the above three pieces constitute a performance quantification tool, with the quantification tool, it can be said that APP performance is good or bad, take cold start as an example, how to formulate the cold start indicators? Technically, sensation can be defined as follows:
Cold start time = from APP process creation to first valid page frame [splash screen]Copy the code
Specifically in terms of implementation, what links are involved and how will it affect the cold start speed?
Cold boot -> The system will start a StartWindow placeholder -> Start the process ->Create Application-> Initialize the global configuration in Application-> Start the first Activity->Create->Start->Resume->AddWindow->UI Measure render [performTraversals]-> first frame visibleCopy the code
Cold startup, the system will generally start a placeholder Window, the default is a white screen Window, reuse is the first to start the Activity configuration, in motion, the main following Activity configuration
<item name="android:windowBackground">@drawable/xxx</item>
Copy the code
It is typically a SplashActivity configuration that uses a brand image as a proxy. It is best to limit the size of this image, otherwise it will affect the startup speed of parsing. After that, the system will start a process to load SplashActivity. The start process mainly involves some global initialization operations of APP in the Application, which may be as light as possible or delayed. Of course, there may also be some ContentProviders and receivers that affect the start of the APP.
public class LabApplication extends Application { @Override public void onCreate() { super.onCreate() <! -- Don't handle time-consuming operations in UI -->}Copy the code
Then comes the Activity creation and startup process:
As you can see, the Activity startup process in the figure above is passive message processing, mainly controlled by AMS command. The process of setting View and display in the code is just a few points in the figure above, such as setting Layout and inflater in onCreate. Of course, this is not necessary. The top-level DecorView is created later in the wm.addView, even if the contentView is not set actively.
@Override public void setContentView(int resId) { ensureSubDecor(); ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content); contentParent.removeAllViews(); <! Inflater.from(mContext).inflate(resId, contentParent); mAppCompatWindowCallback.getWrapped().onContentChanged(); }Copy the code
The inflate of the setContentView may be a point that affects the elapsed time. After that, the handleResumeActivity adds a window View to the WMS
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
if (!a.mWindowAdded) {
a.mWindowAdded = true;
<!--添加Window-->
wm.addView(decor, l);
} else {
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
Copy the code
You can see that getDecorView handles the top-level window creation logic for the Activity. AddView calls Windows ManagerGlobal’s addView to create the ViewRootImpl, which is then used to further add the Window
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { <! -- key --> root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); <! Root. setView(view, wparams, panelParentView); }}Copy the code
After ViewRootImpl takes over the process, all view-related operations are handled in ViewRootImpl, and its requestLayout triggers the measurement, layout, and drawing actions
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { <! RequestLayout (); <! --> mOrigWindowType = mWINDOwattributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); }}}Copy the code
RequestLayout allocates an asynchronous message to receive the VSYNC signal that is triggered by doScheduleCallback. This ensures that all incoming messages are deferred, so that when the Window is added, UI drawing task executes first.
void scheduleTraversals() { if (! mTraversalScheduled) { mTraversalScheduled = true; <! --> mTraversalBarrier = mhandler.getlooper ().getQueue().postSyncbarrier (); <! - insert the drawing request, trigger VSYNC - > mChoreographer. PostCallback (Choreographer. CALLBACK_TRAVERSAL mTraversalRunnable, null); }Copy the code
Wait for the VSYNC message to return, exit the asynchronous message fence, and handle the UI drawing as soon as possible:
As you can see, this logic is basically executed next to addView. Therefore, it can be considered as the first asynchronous message scheduled by the system after Resume, so as long as a message is inserted after onResume, it can actually monitor the first frame rendering, as shown below.
There are other implementations on the web, such as onAttachedToWindow or OnWindowFocusChange after sending messages, or listening directly, that work similarly.
In summary, it can be assumed that a message should be inserted after a Resume. If you have a target, is it met? How to collect them? The baseline? We can refer to industry practices, and the collection method can be non-invasive, and the excellent baseline can be considered as:
Excellent = seconds openCopy the code
If it is not up to standard, the next thing to do is positioning + optimization, which reflects the importance of analysis tools. In fact, the above principle analysis has been made with the help of Studio’s own Profiler tool, which can get twice the result with half the effort in understanding the process.
How do I locate the current performance problem
The time spent at each stage of cold start can be determined by using various tools and methods: You can use debug. startMethodTracing, perfetto/systrace, or even the Studio’s own Profiler, each of which has its own advantages and can be used with choice.
Debug.startmethodtracing is a good place to look at time-consuming functions for UI threads
Debug.startmethodtracing is a method tracing method by applying staking to generate trace logs. However, when profiling is enabled, the application will run slower, so it should not be used to determine the absolute time of profiling data. The most important role is to compare, you can compare before, or compare surrounding functions. Specific usage:
private void startTrace() { File file = new File(getApplication().getExternalFilesDir("android"), "methods.trace"); Debug.startMethodTracing(file.getAbsolutePath(), 100 * 1024 * 1024); } <! --> private void stopTrace() {debug.stopMethodtracing (); }Copy the code
For cold start: The trace file is analyzed in Studio and you can see the key functions exhausted. There are several modes available in Studio, including Flame Chart, Top Down, Bottom Up, and Event. For example, cold start can be divided into several stages, such as process creation, Application initialization, Activity creation, create, resume, draw, etc. First select the Main thread, and then limit the scope to the Application stage, as follows:
- Flame Chart: Focuses more on visualizing the severity of the function’s time consumption
For example, in the figure above, the light yellow part is actually the part that needs to be focused on. The functions that take the most time will be shown first, which is more convenient for locating serious problems. After locating the problems roughly, you can use Top Down to further see the details.
- Top Down: Focuses on detailed search from the Top Down
The top-down mode can be used to observe the function time and call stack more accurately and more clearly. As follows, in the Application initialization stage, the order and time of function call can be clearly seen.
For cold starts, focus on time consuming functions and try to remove non-core logic from the UI thread. Similarly, onCreate for a splash screen Activity is similar to the onResume phase
It can be seen from the figure that some time-consuming operations of Flutterboost and buried Json parsing classes are inadvertently associated with the startup process of Activit, which slows down the cold startup speed, so it can be processed in non-UI threads or postponed.
- -Leonard: Well, you’ve got to Bottom Up.
This model is not used much, listing too many functions, there is no hierarchy, may be alone to see the top several benefits.
Profiler can be relied on to determine which functions are causing slow cold starts, but these functions may not be themselves time-consuming, perhaps due to scheduling or locking, where Perfetto/Systrace can provide more help.
Perfetto/Systrace: The big picture and scheduling
Perfetto address and usage document
Perfetto/Systrace is another performance analysis tool officially provided, in which Perfetto can be seen as an upgraded version of Systrace. MethodTracing is not specific to every method compared to MethodTracing, but it provides a global performance overview and can locate problem areas more quickly. Perfetto/Systrace has advantages in global task scheduling and system calls. MethodTracing has some impact on performance. Perfetto/Systrace can reduce the impact of the system itself by using the system lOG. Use Perfetto to see the process of cold startup as follows:
As shown in the figure, first of all, you can intuitively see which stages are time-consuming. Then, you can make directional analysis, shrink the time period and zoom in for observation:
It can be seen that the resource resolution of the blue mark takes too long when the Activity is started, and the map is found to be large after directional investigation
Name res/BKC.xml
Category null
Start time 1s 309ms 568us 459ns
Duration 42ms 35us 682ns
Slice ID 2465
Copy the code
The graph was appropriately scaled down to reduce the loading cost and was optimized to 8ms
Name res/BKC.xml
Category null
Start time 964ms 602us 749ns
Duration 8ms 260us 261ns
Slice ID 11851
type internal_slice
Copy the code
For example, for some stages, the UI thread inexplicable sleep, in fact, it can be more convenient to check what factors are caused by, as follows:
As shown above, in the current phase, the UI thread goes to sleep because it has not acquired the lock, and then it is awakened by another thread. This can be used to determine what the problem is and whether it can be avoided.
For overall cold start optimization: use Perfetto to see more directly
Before optimization: 1261ms
Optimized: 439ms
conclusion
Bugs are inevitable, optimizations are persistent, and using the tools well is critical.