{"record":{"id":"6c76c0078931786d","repo":"Tencent/matrix","slug":"must-be-init-in-main-thread","errorCode":null,"errorMessage":"must be init in main thread!","messagePattern":"must be init in main thread!","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-trace-canary/src/main/java/com/tencent/matrix/trace/core/UIThreadMonitor.java","lineNumber":111,"sourceCode":"    private long[] queueCost = new long[CALLBACK_LAST + 1];\n    private static final int DO_QUEUE_DEFAULT = 0;\n    private static final int DO_QUEUE_BEGIN = 1;\n    private static final int DO_QUEUE_END = 2;\n    private boolean isInit = false;\n\n    public static UIThreadMonitor getMonitor() {\n        return sInstance;\n    }\n\n    public boolean isInit() {\n        return isInit;\n    }\n\n    public void init(TraceConfig config) {\n        this.config = config;\n\n        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {\n            throw new AssertionError(\"must be init in main thread!\");\n        }\n\n        boolean historyMsgRecorder = config.historyMsgRecorder;\n        boolean denseMsgTracer = config.denseMsgTracer;\n\n        LooperMonitor.register(new LooperMonitor.LooperDispatchListener(historyMsgRecorder, denseMsgTracer) {\n            @Override\n            public boolean isValid() {\n                return isAlive;\n            }\n\n            @Override\n            public void dispatchStart() {\n                super.dispatchStart();\n                UIThreadMonitor.this.dispatchBegin();\n            }\n\n            @Override","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-trace-canary/src/main/java/com/tencent/matrix/trace/core/UIThreadMonitor.java#L93-L129","documentation":"UIThreadMonitor.init(config) throws an AssertionError when invoked from any thread other than the main (UI) thread. The monitor installs a Looper printer and observes frame callbacks on the main Looper, so initialization off the main thread is unsupported and would produce broken instrumentation.","triggerScenarios":"Calling UIThreadMonitor.get().init(config) from a worker thread, background executor, or a non-main handler thread instead of the application main thread (e.g. inside Application.onCreate is fine, but inside an AsyncTask.doInBackground is not).","commonSituations":"Initializing Matrix/TracePlugin from a background thread at app startup; test harnesses initializing the monitor on a test thread; libraries wrapping Matrix init in their own thread pools.","solutions":["Call init on the main thread, e.g. in Activity/Application.onCreate or via Handler(Looper.getMainLooper()).post(...)","Move TracePlugin initialization into the main app entry point","If init is triggered asynchronously, route it through the main Looper before calling"],"exampleFix":"// before\nnew Thread(() -> UIThreadMonitor.get().init(config)).start();\n// after\nnew Handler(Looper.getMainLooper()).post(() ->\n    UIThreadMonitor.get().init(config));","handlingStrategy":"validation","validationCode":"if (Looper.myLooper() != Looper.getMainLooper()) throw new IllegalStateException(\"call init on main thread\");","typeGuard":"boolean isMainThread() { return Looper.myLooper() == Looper.getMainLooper(); }","tryCatchPattern":"Runnable init = () -> UIThreadMonitor.get().init(config); if (isMainThread()) init.run(); else new Handler(Looper.getMainLooper()).post(init);","preventionTips":["Initialize Matrix plugins inside Application.onCreate","Route any async init through the main Looper","Never initialize UI-coupled monitors from worker threads","Document main-thread requirements in your init wrapper"],"tags":["android","main-thread","ui-thread","matrix","assertion"],"backgroundTag":"invalid-argument-value","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}