This article was first published in the wechat public account “Android Development Journey”, welcome to follow
Wan-android Jetpack VersionAndroid Jetpack architecture for componentized application developmentWelcome to star
Project address of Flutter wan-AndroidFlutter Wan – AndroidWelcome to star
Caton introduction
When users use our application, many problems are difficult to be found in time, such as high memory consumption and traffic consumption, but once the lag occurs, users will intuitively feel it. So application lag is very important to the user experience. On the other hand, it can be difficult for developers to locate the lag problem, which can occur for a variety of reasons: code problems, memory problems, drawing problems, IO operations, etc. What’s more, it is difficult for us to repeat the problem of delay occurring online offline, because it has a great relationship with the user’s system environment at that time. Therefore, we need to record the user’s use scenario when the user sends delay. For example: memory consumption, disk space, user behavior path, etc.
Optimization tools
CPU Profile
Android Studio now comes with a CPU Profiler tool that graphically displays execution times, call stacks, and more. The information collected is comprehensive and covers all threads. However, due to its comprehensive collection of information, it leads to serious memory overhead at runtime, and the overall operation of App functions will be slow, which may lead to deviation of our optimization direction.
Usage: debug.startMethodTracing (); . Debug.stopMethodTracing(); The resulting file in sd card: Android/data/packagename/files directory.
Systrace
Systrace, as explained in previous articles, is a lightweight framework with low overhead that can directly reflect CPU utilization and on the right, ALTER can provide suggestions for some problems. Slow drawing, frequent GC, etc.
StrictMode
A tool class introduced in Android2.3: rigor mode. Is a runtime detection mechanism. Helps developers detect irregularities in code. StrictMode checks thread policies and VM policies.
Thread policies include:
-
Custom time-consuming call, detectCustimSlowCalls
-
Disk read operation, detectDiskReads
-
Network operation, detectNetwork
Vm Policies:
-
ActivityLeak, DetectacvityLeaks
-
Sqlite object leak, detectLeakedSqlLiteObjects
-
Check the number of instances, setClassInstanceLimit
In Application we use:
private void initStrictMode() {
if(BuildConfig. DEBUG) {/ / thread StrictMode. SetThreadPolicy (new StrictMode. ThreadPolicy. Builder () detectCustomSlowCalls () //API level 11, Use strictmode.noteslowcode.detectdiskreads ().detectdiskwrites ().detectnetwork ()// or use.detectall () to detectAll information about the phone .build()); / / virtual machine StrictMode. SetVmPolicy (new StrictMode. VmPolicy. Builder () detectLeakedSqlLiteObjects () SetClassInstanceLimit (StrictModeTest. Class, 1) detectLeakedClosableObjects () / / API grade 11. PenaltyDropBox (). The build ()); }}Copy the code
StrictMode itself is performance-intensive, so we only enable it in Debug mode. When it does not conform to the detection policy, it will print logs on the console. Enter StrictMode keyword to filter.
Automatic detection of stuck method
Both CPU Profiler and Systrace are intended for offline use, not online. So how do we detect congestion online?
As we all know that there is only one Looper object in a process, we found by checking the Looper source code that there is a mLogging object in the loop method in the loop method, and a peer log is printed in the execution. A Finished to log is generated. Such as:
public static void loop() {/ /... Omit the start code...for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if(logging ! = null) {// Emphasis starts printing logging.println(">>>>> Dispatching to " + msg.target + "" +
msg.callback + ":"+ msg.what); } / /... Omit intermediate code...if(logging ! = null) {// Focus on printing logging.println("<<<<< Finished to " + msg.target + ""+ msg.callback); } / /... Omit the last code... }}Copy the code
So we can define the regular Printer object, so that the logs of the Handler are all printed through our self-defined Printer, and then collect the log information, matching the Dispatching to and Finished to fields. If the peer is Dispatching only the peer to field but not the Finished TO field in a specified time, stutter occurs. Call stack information is collected after a lag occurs. Conversely, if both fields are present, the application is running smoothly.
Field Printer set to mLogging object:
Looper.getMainLooper().setMessageLogging(new Printer() {
@Override
public void println(String log) {
Log.e("printer"."==println=="+log); }});Copy the code
The Log fields in the code are the Dispatch and Finished fields we need to monitor and collect call stack information and send it to the back end for analysis.
So there’s a problem here that maybe the information we collect isn’t accurate enough. Why? That is, the call stack information we collected is the last to be collected, so it is possible that the execution of Caton has been completed by this time, and the information collected at this moment may not be the key information of caton. Like the OOM, it’s one that could happen at any time. Therefore, we need to collect the log information at a high frequency. The high frequency of collecting the log information will put a certain pressure on the back end, and a large part of the information we collect at a high frequency is also repeated, so we need the log to redo the operation.
Abnormal ANR
ANR exception full name Application Not Responding. If your application has been unresponsive for a period of time, the user will be presented with a dialog box called the Application Unresponsive dialog box. The user can choose to “wait” and let the application continue, or “force close”. So a smooth, sensible application cannot have ANR. Because this affects the user experience, of course, due to the manufacturer’s deep customization system, ANR will not pop on some mobile phones.
The time between the ANR and the pop-up is defined differently in different components. The key is 5 seconds. The foreground broadcast is 10 seconds, the backstage broadcast is 60 seconds. The front desk service takes 20 seconds and the back desk service takes 200 seconds. This data is defined in AMS, which you can check out.
The process of ANR execution:
The ANR logs are stored in the data/ ANR/testamp. TXT directory.
We can view logs offline directly with ADB command:
Adb pull data/anr/ localted. TXT your directory.
Online we can use FileObserver to monitor file changes, but this approach has permissions issues on older systems. Another option is to use the AnrWatchDog framework. This is also an open source framework at github.com/SalomonBrys… Its principle is to modify the value of the way to determine whether the UI thread has stalled.
This library is also very simple to use. First configure it in Gradle:
compile 'com. Making. Anrwatchdog: anrwatchdog: 1.4.0'
Copy the code
Then initialize it in Application:
new ANRWatchDog().start();
Copy the code
That will do. By default, an ANRError will crash the application if it detects a lag. We can override the Listener interface to fetch stack information.
ANRWatchDog inherits the Thread, so let’s look at the code logic in the core method run.
// Post operations private final Runnable _ticker = newRunnable() {
@Override public void run() {
_tick = 0;
_reported = false; }};Copy the code
@Override
public void run() {// First rename the threadsetName("|ANR-WatchDog|");
long interval = _timeoutInterval;
while(! isInterrupted()) { boolean needPost = _tick == 0; _tick += interval;if(needPost) {// Send post _uiHandler.post(_ticker); } try {// sleep thread.sleep (interval); } catch (InterruptedException e) { _interruptionListener.onInterrupted(e);return ;
}
// If the main thread has not handled _ticker, it is blocked. ANR.
if(_tick ! = 0 &&! _reported) { //noinspection ConstantConditionsif(! _ignoreDebugger && (Debug.isDebuggerConnected() || Debug.waitingForDebugger())) { Log.w("ANRWatchdog"."An ANR was detected but ignored because the debugger is connected (you can prevent this with setIgnoreDebugger(true))");
_reported = true;
continue ;
}
interval = _anrInterceptor.intercept(_tick);
if (interval > 0) {
continue;
}
final ANRError error;
if(_namePrefix ! = null) { error = ANRError.New(_tick, _namePrefix, _logThreadsWithoutStackTrace); }else {
error = ANRError.NewMainOnly(_tick);
}
_anrListener.onAppNotResponding(error);
interval = _timeoutInterval;
_reported = true; }}}Copy the code
ANRWatchDog is used because it is non-invasive and compensates for high version permissions. A combination of the two.
This is the method of applying the trap detection. So specific how to avoid the lag, which requires us to develop good code habits in the peacetime development. Write high quality code. Please see the layout optimization in the review below.
Scan the qr code below to follow the public account, timely access to the article push.
Phase to recommend
Android performance optimization layout optimization practice
Startup optimization of Android performance optimization