This is the sixth day of my participation in the November Gwen Challenge. See details: The Last Gwen Challenge 2021.

BlockMoonlightTreasureBox caton Pandora’s box

We concluded earlier

App Caton series 1: Handler synchronization barrier

App Caton series II: Learning screen refresh mechanism

App Caton series three: ANR principle

App Caton series iv: Toutiao Caton monitoring scheme landing

Finally, it’s time for the final release

Technical theory comes from:

Toutiao ANR Optimization Practice series – Design principles and influencing factors

Toutiao ANR Optimization Practice series – Monitoring tools and analysis ideas

use

Introduction:

Implementation 'IO. Making. Xiaolutang: BlockMoonlightTreasureBox: 1.0.0'Copy the code

Initialization:

The initiation of the Luna Box is divided into two parts

  1. BlcokMonitor initialization
  2. Native listener initialization
BlockBoxConfig blockMonitorface.init (this).updateconfig (new BlockBoxconfig.Builder ()) .useAnalyze(true) .build()) .startMonitor(); / / registered native monitoring SIGNAL SystemAnrMonitor QUIT SIGNAL. The init (BlockMonitorFace. GetBlockMonitorFace ());Copy the code

BlockBoxConfig#Builder

Issue warning messages and aggregate the main thread messages separately

SetGapTime: A gap message will be generated when the main thread is in ideL state for longer than this time. If the ideL state is smaller than this value, no separate processing is performed

SetAnrTime: AnR is determined to have occurred when the main thread message is dispatched for more than this time

SetJankFrame: can be simply understood as warning when the View drawing process takes more than three frames. App Caton series four: Toutiao Caton monitoring scheme landing

UseAnalyze: Specifies whether to generate an analysis application icon on the desktop. A typical scenario is that a test version can be built, but a release version is not

AddAnrSampleListener: Adds listening ANR information. A lot of ANR files occur online. If there are only offline ANR logs, it is obviously not conducive to analyzing ANR, so this interface is provided.

The interface receives an IAnrSamplerListener

/ * * * from ISampleListener specialized information acquisition anr callback * / public interface IAnrSamplerListener extends IMainThreadSampleListener {/ * * * Collect unprocessed messages in the message queue */ void onMessageQueueSample(long baseTime, String msgId, String MSG); void onCpuSample(long baseTime, String msgId, String msg); void onMemorySample(long baseTime, String msgId, String msg); void onMainThreadStackSample(long baseTime, String msgId, String msg); } / * * * all these method calls in the main thread * take care not to make time-consuming operation * / public interface IMainThreadSampleListener {/ * * * the current main thread scheduling ability * * @ param start true */ void onScheduledSample(Boolean start, Long baseTime, String msgId, Long Dealt); Void onMsgSample(long baseTime, String msgId, MessageInfo MSG); void onJankSample(String msgId, MessageInfo msg); / * * * anr's message has been processed in the message queue * * / void messageQueueDispatchAnrFinish (); void onSampleAnrMsg(); }Copy the code

Among them is onSampleAnrMsg and messageQueueDispatchAnrFinish need special attention. OnSampleAnrMsg means received ANR news, information can be stored at this time (if no messageQueueDispatchAnrFinish behind the news, can use this as the basis of analysis). When receiving the messageQueueDispatchAnrFinish mean the end of the ANR news gathering.

Native to initialize

System ANR uses SIGNAL QUIT SIGNAL to monitor. Here, we replace the system’s monitor with our own registration and return the SIGNAL to the system after the message processing is completed.

However, the location of the Moonlight box is a complement to the ANR message, in order to be compatible with other frameworks that handle ANR by listening for SIGNAL QUIT signals

SystemAnrMonitor. Init (BlockMonitorFace getBlockMonitorFace ()) to register notice BlockMonitor received ANR news.

​
​
public class SystemAnrMonitor {
    static {
        System.loadLibrary("block_signal");
    }
    private native void hookSignalCatcher(ISystemAnrObserver observed);
    private native void unHookSignalCatcher();
​
    public static void init(ISystemAnrObserver systemAnrObserver){
        new SystemAnrMonitor().hookSignalCatcher(systemAnrObserver);
    }
}
​
public interface ISystemAnrObserver {
    void onSystemAnr();
}
Copy the code

With other frameworks, you simply call onSystemAnr when listening for the system ANR.

instructions

Although IAnrSamplerListener has CPU and Memory information collection callbacks, there is no corresponding implementation of Moonlight Box, because moonlight box is a complement to the system ANR information, not a corresponding replacement. When ANR occurs in the system, it is more convenient to analyze ANR with the information collected by the system and moonlight box.

Case study:

Reference project Test module, mainly tested three contents

Jank frame drop

We hibernate for 500ms in View’s Draw method to observe the log

@Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        long start = SystemClock.elapsedRealtime();
        if(jank){
            SystemClock.sleep(500);
        }
        long end = SystemClock.elapsedRealtime();
        Log.d("JankView","BlockMonitor start "+start + "  end "+end + "   "+(end - start));
    }
Copy the code

Log message:

Time spent on a single message, ANR

We send a long runnable to the message queue, and then send a foreground broadcast to test it. Finally, we found that two ANR records were generated

The reason is that when a message queue processes a message, we discover the ANR first and then, or the moonlight box, or the system triggers the ANR end message. Because this termination is triggered twice, two messages are generated.

The message is sent through the instance object of Android.os. Hander, where callback is an internal class in MainActivity, and there are still two messages waiting to be processed in the message queue. Time stack hit Thread.sleep

The second log is not parsed in this position because the first log uses the information collected. The second log does not have much information available, but if a main thread Looper is found to take longer than the specified ANR time (3s by default), then this method should always take precedence.

Message queue messages are frequent, ANR

We simulate the main thread message queue being busy by sending more time-consuming tasks to the main thread

findViewById(R.id.tvTestAnr2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { For (int I =25; i>0; i--){ mainHandler.post(new Runnable() { @Override public void run() { //500ms SystemClock.sleep(500); }}); } AnrTestBroadcast.sentBroadcast(MainActivity.this); }});Copy the code

Results analysis:

So this is a reflection of, a lot of times we may not be doing time-consuming operations, but ANR is happening from the system side.

Other threads or processes preempt the CPU, resulting in insufficient main thread scheduling capability

This is not very easy to simulate

How to expand online log collection

Reference BlockBoxConfig# Builder

Technical Exchange Group:

Code Address:

Github.com/xiaolutang/…

\