The background,

Matrix-android currently monitors the following areas: application package size, frame rate variation, startup time, lag, slow methods, SQLite operation optimization, file read and write, memory leaks, etc.

Trace-canary contains four parts: frame rate FPS, startup time, lag, and ANR.

As the whole system of Matrix is very large, we try to interpret it separately. Lest cause heart pressure ~

Start with the initialization process

Start with the initialization process used by Matrix. Focus on the numbers annotated in the comments.

// Switch. DynamicConfigImplDemo dynamicConfig = new DynamicConfigImplDemo(); sContext = this; MatrixLog.i(TAG, "Start Matrix configurations."); Matrix.Builder Builder = new matrix. Builder(this); // Reporter. Matrix will callback this listener when found issue then emitting it. Contains life cycles such as init, start, Stop, destroy, and the onReportIssue callback when a problem is detected. builder.pluginListener(new TestPluginListener(this)); // 3, Configure trace canary. TracePlugin TracePlugin = configureTracePlugin(dynamicConfig); builder.plugin(tracePlugin); / /... //4, create plugins when build, which calls plugin init method. Matrix.init(builder.build()); Start () traceplugin.start (); MatrixLog.i(TAG, "Matrix configurations done.");Copy the code

Brief analysis:

  1. buildMatrix.BuilderTo register the required plug-ins. The Builder is not required and a plugin can be initialized separately
  2. Register plugin’s lifecycle callback:init(),start(),stop(),destroy(), and the callback if a problem is detected 🙁onReportIssue(Issue issue))
  3. callconfigureTracePlugin()Methods buildtracePluginObject returns, register
  4. To mobilizebuild()generatematrixObject that calls all registered pluginsinitmethods
  5. TracePlugin callstart()methods

2.1 configureTracePlugin () method

Private TracePlugin configureTracePlugin(DynamicConfigImplDemo dynamicConfig) {// Config switch Boolean fpsEnable = dynamicConfig.isFPSEnable(); boolean traceEnable = dynamicConfig.isTraceEnable(); boolean signalAnrTraceEnable = dynamicConfig.isSignalAnrTraceEnable(); File traceFileDir = new File(getApplicationContext().getFilesDir(), "matrix_trace"); if (! traceFileDir.exists()) { if (traceFileDir.mkdirs()) { MatrixLog.e(TAG, "failed to create traceFileDir"); } // create anR folder to store ANR files File anrTraceFile = new File(traceFileDir, "anr_trace"); / / path: / data/user / 0 / sample. Tencent. The matrix/files/matrix_trace/anr_trace/folder/create log print?? File printTraceFile = new File(traceFileDir, "print_trace"); // path : /data/user/0/sample.tencent.matrix/files/matrix_trace/print_trace TraceConfig traceConfig = new TraceConfig.Builder() .dynamicConfig(dynamicConfig) .enableFPS(fpsEnable) .enableEvilMethodTrace(traceEnable) // anr? .enableAnrTrace(traceEnable) .enableStartup(traceEnable) .enableIdleHandlerTrace(traceEnable) // Introduced in Matrix 2.0, Newly Introduced idleHandler monitoring. EnableMainThreadPriorityTrace (true) / / Introduced in 2.0 the new Matrix Introduced main thread priority monitoring . EnableSignalAnrTrace (signalAnrTraceEnable) // Introduced in Matrix 2.0 anR monitoring, native ANR?? .anrTracePath(anrTraceFile.getAbsolutePath()) .printTracePath(printTraceFile.getAbsolutePath()) .splashActivities("sample.tencent.matrix.SplashActivity;" ) / /?? .isDebug(true) .isDevEnv(false) .build(); //Another way to use SignalAnrTracer separately //useSignalAnrTraceAlone(anrTraceFile.getAbsolutePath(), printTraceFile.getAbsolutePath()); Return new TracePlugin(traceConfig); }Copy the code

The main work is to configure various functions of tracePlugin, which can be made according to the needs of their own projects.

2.2 Matrix.Build Build () method

The build() method eventually creates the matrix object.

public Matrix build() {
    if (pluginListener == null) {
        pluginListener = new DefaultPluginListener(application);
    }
    return new Matrix(application, pluginListener, plugins);
}
Copy the code
private Matrix(Application app, PluginListener listener, HashSet<Plugin> plugins) { this.application = app; this.pluginListener = listener; this.plugins = plugins; / / 1, AppActiveMatrixDelegate is an enumeration type, register the activity callbacks and onLowMemory callback AppActiveMatrixDelegate. The INSTANCE. The init (application); Plugin.init (application, pluginListener); plugin.init(application, pluginListener); OnInit (pluginListener.oninit); }}Copy the code

2.2.1 AppActiveMatrixDelegate. Init (Application Application)

Two functions:

  • Registered registerComponentCallbacks callback, the purpose is to monitor system onTrimMemory callback
  • Register activity lifecycle callbacks to maintain a foreground and background state
public void init(Application application) { if (isInit) { MatrixLog.e(TAG, "has inited!" ); return; } this.isInit = true; if (null ! = MatrixHandlerThread.getDefaultHandlerThread()) { this.handler = new Handler(MatrixHandlerThread.getDefaultHandlerThread().getLooper()); } application.registerComponentCallbacks(controller); application.registerActivityLifecycleCallbacks(controller); }Copy the code

RegisterActivityLifecycleCallbacks () function:

Manage and maintain the foreground and background state of App, and call the onForeground(Boolean foreground) method of IAppForeground in onDispatchForeground(), because plugin implements this interface. Therefore, each plugin is aware of foreground and background state transitions.

Note that this callback is in a child thread named default_matrix_thread. This thread is the worker thread of matrix and is managed by the MatrixHandlerThread class.

@Override public void onActivityStarted(Activity activity) { updateScene(activity); onDispatchForeground(getVisibleScene()); } @Override public void onActivityStopped(Activity activity) { if (getTopActivityName() == null) { onDispatchBackground(getVisibleScene()); }}Copy the code
@Override public void onTrimMemory(int level) { MatrixLog.i(TAG, "[onTrimMemory] level:%s", level); if (level == TRIM_MEMORY_UI_HIDDEN && isAppForeground) { // fallback onDispatchBackground(visibleScene); }}Copy the code

2.2.2 Calling init method of plugin

Here we look specifically at the init method of the tracePlugin.

@Override public void init(Application app, PluginListener listener) { super.init(app, listener); MatrixLog.i(TAG, "trace plugin init, trace config: %s", traceConfig.toString()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { MatrixLog.e(TAG, "[FrameBeat] API is low Build.VERSION_CODES.JELLY_BEAN(16), TracePlugin is not supported"); unSupportPlugin(); return; } looperAnrTracer = new looperAnrTracer (traceConfig); frameTracer = new FrameTracer(traceConfig); evilMethodTracer = new EvilMethodTracer(traceConfig); startupTracer = new StartupTracer(traceConfig); }Copy the code

Specific logic behind analysis, here first look at the main process. The tracer subclasses will be introduced separately later.

In addition, TracePlugin inherits the Plugin base class. The init method of the parent Plugin:

@Override public void init(Application app, PluginListener listener) { if (application ! = null || pluginListener ! = null) { throw new RuntimeException("plugin duplicate init, application or plugin listener is not null"); } status = PLUGIN_INITED; this.application = app; this.pluginListener = listener; // Listen for the state switch between front and back. AppActiveMatrixDelegate.INSTANCE.addListener(this); }Copy the code

Brief analysis:

Perceiving the switch between front and back state.

2.3 Matrix. The init (Matrix Matrix)

After seeing the plugins registration and initialization work, it was time to actually implement the specific logic of each plugin.

So, we need to go back to the init(Matrix Matrix) method of Matrix in the main process initialization.


private static volatile Matrix sInstance;

public static Matrix init(Matrix matrix) {
    if (matrix == null) {
        throw new RuntimeException("Matrix init, Matrix should not be null.");
    }
    synchronized (Matrix.class) {
        if (sInstance == null) {
            sInstance = matrix;
        } else {
            MatrixLog.e(TAG, "Matrix instance is already set. this invoking will be ignored");
        }
    }
    return sInstance;
}
Copy the code

Nothing, referred to by the static variable sInstance, for later use.

Iii. Responsibilities of class

After the above analysis, from the perspective of matrix top-level design, the classes involved are simply sorted out:

conclusion

The Matrix class is the entry point to the initialization, managing plugins for different functions and calling back the life cycle and detection results of the caller, the app-side plug-in.

The AppActiveMatrixDelegate class is responsible for listening to changes in the foreground and background and maintaining a foreground and background state. Notify the listener if necessary.