Matrix卡顿个人心得

/ 0评 / 0

Matrix源码分析

卡顿监控原理

替换主线程Looper的Printer,从而监控dispatchMessage的执行时间,源码可以查看com/tencent/matrix/trace/core/LooperMonitor.java

具体可以查看公众号里面微信团队的解析

通过监控dispatchMessage的执行时间有三个缺点

IdleHandler的耗时无法被监控到

Matrix的解决方案是反射替换了IdleHandler,源码可以查看com/tencent/matrix/trace/tracer/IdleHandlerLagTracer.java

Touch的卡顿无法监控到

Matrix的方案是send和recv方法来监控Touch事件啊,源码可以查看com/tencent/matrix/trace/tracer/TouchEventLagTracer.java

有一个低成本的方案

class TouchCostWindowCallback(private val windowCallback: Window.Callback) :
    Window.Callback by windowCallback {
    override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        val start = System.currentTimeMillis()
        val rst = windowCallback.dispatchTouchEvent(event)
        val cost = System.currentTimeMillis() - start
        // 监控
        return rst
    }
}

使用方案如下

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                val originalCallback = activity.window.callback
                val wrappedCallback = TouchCostWindowCallback(originalCallback)
                activity.window.callback = wrappedCallback
            }
            override fun onActivityStarted(activity: Activity) {}
            override fun onActivityResumed(activity: Activity) {}
            override fun onActivityPaused(activity: Activity) {}
            override fun onActivityStopped(activity: Activity) {}
            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
            override fun onActivityDestroyed(activity: Activity) {}
        })
    }
}

SyncBarrier泄露

这里直接复制公众号里面的内容:目前我们的方案是不断轮询主线程LooperMessageQueuemMessage(也就是主线程当前正在处理的Message)。而SyncBarrier本身也是一种特殊的Message,其特殊在它的target是null。如果我们通过反射mMessage,发现当前的Message的target为null,并且通过这个Message的when发现其已经存在很久了,这个时候我们合理怀疑产生了SyncBarrier的泄漏(但还不能完全确定,因为如果当时因为其他原因导致主线程卡死,也可能会导致这种现象),然后再发送一个同步消息和一个异步消息,如果异步消息被处理了,但是同步消息一直无法被处理,这时候就说明产生了SyncBarrier的泄漏。如果激进一些,这个时候我们甚至可以反射调用MessageQueueremoveSyncBarrier方法,手动把这个SyncBarrier移除掉,从而从错误状态中恢复。下面代码展示了大概的原理:

MessageQueue mainQueue = Looper.getMainLooper().getQueue();
Field field = mainQueue.getClass().getDeclaredField("mMessages");
field.setAccessible(true);
Message mMessage = (Message) field.get(mainQueue);  //通过反射得到当前正在等待执行的Message
if (mMessage != null) {
    currentMessageToString = mMessage.toString();
    long when = mMessage.getWhen() - SystemClock.uptimeMillis();
    if (when < -3000 && mMessage.getTarget() == null) { //target == null则为sync barrier
        int token = mMessage.arg1;
        startCheckLeaking(token);
    }
}

private static void startCheckLeaking(int token) {
    int checkCount = 0;
    barrierCount = 0;
    while (checkCount < CHECK_STRICTLY_MAX_COUNT) {
        checkCount++;
        int latestToken = getSyncBarrierToken();
        if (token != latestToken) {     //token变了,不是同一个barrier,return
            break;
        }
        if (DetectSyncBarrierOnce()) {
            //发生了sync barrier泄漏
            removeSyncBarrier(token);   //手动remove泄漏的sync barrier
            break;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private static void removeSyncBarrier(int token) {
    MessageQueue mainQueue = Looper.getMainLooper().getQueue();
    Method method = mainQueue.getClass().getDeclaredMethod("removeSyncBarrier", int.class);
    method.setAccessible(true);
    method.invoke(mainQueue, token);
}

private static boolean DetectSyncBarrierOnce() {
    Handler mainHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.arg1 == 0) {
                barrierCount ++;    //收到了异步消息,count++
            } else if (msg.arg1 == 1) {
                barrierCount = 0;   //收到了同步消息,说明同步屏障不在, count设置为0
            }
        }
    };

    Message asyncMessage = Message.obtain();
    asyncMessage.setAsynchronous(true);
    asyncMessage.setTarget(mainHandler);
    asyncMessage.arg1 = 0;

    Message syncNormalMessage = Message.obtain(); 
    syncNormalMessage.arg1 = 1;

    mainHandler.sendMessage(asyncMessage);      //发送一个异步消息
    mainHandler.sendMessage(syncNormalMessage); //发送一个同步消息

    if(barrierCount > 3){
        return true;
    }
    return false;
}

简单版本的Matrix

class LagException(s: String) : Exception(s)
class AnrException(s: String) : Exception(s)
class IdleLagException(s: String) : Exception(s)

object MsgMonitor {
    private const val DEFAULT_ANR = 5 * 1000L
    private const val DEFAULT_NORMAL_LAG = 2 * 1000L
    private const val DEFAULT_IDLE_HANDLER_LAG = DEFAULT_NORMAL_LAG

    private val msgMonitorHandler by lazy {
        val thread = HandlerThread("MsgMonitor")
        thread.start()
        Handler(thread.looper)
    }

    private val idleMsgHandler by lazy {
        val thread = HandlerThread("idleMsgMonitor")
        thread.start()
        Handler(thread.looper)
    }

    private val lagReportTask: Runnable by lazy {
        Runnable {
            val stackTrace = getMainThreadStackTrace()
            val lagException = LagException("卡顿异常")
            lagException.stackTrace = stackTrace
            Log.i("MsgMonitor", Log.getStackTraceString(lagException))
        }
    }

    private val anrReportTask: Runnable by lazy {
        Runnable {
            val stackTrace = getMainThreadStackTrace()
            val anrException = AnrException("ANR异常")
            anrException.stackTrace = stackTrace
            Log.i("MsgMonitor", Log.getStackTraceString(anrException))
        }
    }

    private val idleReportTask: Runnable by lazy {
        Runnable {
            val stackTrace = getMainThreadStackTrace()
            val idleLagException = IdleLagException("IdleLag卡顿")
            idleLagException.stackTrace = stackTrace
            Log.i("MsgMonitor", Log.getStackTraceString(idleLagException))
        }
    }

    private val touchReportTask: Runnable by lazy {
        Runnable {
            val stackTrace = getMainThreadStackTrace()
            val idleLagException = TouchLagException("touch卡顿")
            idleLagException.stackTrace = stackTrace
            Log.i("MsgMonitor", Log.getStackTraceString(idleLagException))
        }
    }

    fun init() {
        hookLooper()
        hookIdleHandler()
    }

    fun injectTouch(window: Window) {
        val originalCallback = window.callback
        val wrappedCallback = TouchCostWindowCallback(originalCallback)
        window.callback = wrappedCallback
    }

    private fun hookLooper() {
        Looper.getMainLooper().setMessageLogging { x ->
            val isValid = x[0] == '>' || x[0] == '<'
            if (isValid) {
                dispatch(x[0] == '>', x)
            }
        }
    }

    private fun hookIdleHandler() {
        try {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                return
            }
            val mainQueue = Looper.getMainLooper().queue
            val field = MessageQueue::class.java.getDeclaredField("mIdleHandlers")
            field.isAccessible = true
            val myIdleHandlerArrayList = IdleArrayList()
            field[mainQueue] = myIdleHandlerArrayList
        } catch (t: Throwable) {
            Log.i("MsgMonitor", "hook fail")
        }
    }

    private fun dispatch(isBegin: Boolean, log: String) {
        if (isBegin) {
            msgMonitorHandler.postDelayed(lagReportTask, DEFAULT_NORMAL_LAG)
            msgMonitorHandler.postDelayed(anrReportTask, DEFAULT_ANR)
        } else {
            msgMonitorHandler.removeCallbacks(lagReportTask)
            msgMonitorHandler.removeCallbacks(anrReportTask)
        }
    }

    private fun getMainThreadStackTrace(): Array<out StackTraceElement> {
        val mainThread = Looper.getMainLooper().thread
        return mainThread.stackTrace
    }

    private class IdleHandlerProxy(val idleHandler: MessageQueue.IdleHandler) :
        MessageQueue.IdleHandler {
        override fun queueIdle(): Boolean {
            idleMsgHandler.postDelayed(idleReportTask, DEFAULT_IDLE_HANDLER_LAG)
            val ret = idleHandler.queueIdle()
            idleMsgHandler.removeCallbacks(idleReportTask)
            return ret
        }

        override fun toString(): String {
            return idleHandler.toString()
        }
    }

    class IdleArrayList : ArrayList<Any>() {

        private var map: MutableMap<IdleHandler, IdleHandlerProxy> =
            HashMap()

        override fun add(element: Any): Boolean {
            if (element is IdleHandler) {
                val myIdleHandler = IdleHandlerProxy(element)
                map[element] = myIdleHandler
                return super.add(myIdleHandler)
            }
            return super.add(element)
        }

        override fun remove(element: Any): Boolean {
            return if (element is IdleHandlerProxy) {
                val idleHandler: IdleHandler = element.idleHandler
                map.remove(idleHandler)
                super.remove(element)
            } else {
                val myIdleHandler: IdleHandlerProxy? = map.remove(element)
                if (myIdleHandler != null) {
                    super.remove(myIdleHandler)
                } else super.remove(element)
            }
        }
    }

    internal class TouchCostWindowCallback(private val windowCallback: Window.Callback) :
        Window.Callback by windowCallback {
        override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
            val start = System.currentTimeMillis()
            msgMonitorHandler.postDelayed(touchReportTask, DEFAULT_NORMAL_LAG)
            val rst = windowCallback.dispatchTouchEvent(event)
            msgMonitorHandler.removeCallbacks(touchReportTask)
            val cost = System.currentTimeMillis() - start
            return rst
        }
    }

}

其实在ANR的时候,我们也可以检查一下当前的消息,封装如下


object MsgUtils {

    fun dumpMsgQueue() {
        try {
            // 获取主线程的Looper
            val looper = Looper.getMainLooper()

            // 获取MessageQueue
            val messageQueue = looper.queue

            // 反射获取MessageQueue中的mMessages字段
            val messagesField = MessageQueue::class.java.getDeclaredField("mMessages")
            messagesField.isAccessible = true

            // 反射获取MessageQueue中的next()方法
            val nextMethod = MessageQueue::class.java.getDeclaredMethod("next")
            nextMethod.isAccessible = true

            // 获取mMessages字段的首个Message
            var message = messagesField[messageQueue]

            // 遍历Message链表
            while (message != null) {
                // 打印Message信息
                Log.d("MsgMonitor", message.toString())

                // 通过反射获取下一个Message
                val nextField = message.javaClass.getDeclaredField("next")
                nextField.isAccessible = true
                message = nextField[message]
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun dumpMsgQueue2(): String {
        val printer = DumpPrinter()
        Looper.getMainLooper().dump(printer, "")
        val pendingMsgStr = printer.stringBuilder.toString()
        Log.d("MsgMonitor", pendingMsgStr)
        return pendingMsgStr
    }

    class DumpPrinter : Printer {
        val stringBuilder = StringBuilder()
        override fun println(x: String) {
            stringBuilder.append(x).append("\n")
        }
    }

    fun dumpPendingMsg() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return
        }
        try {
            // 获取主线程的Looper
            val looper = Looper.getMainLooper()

            // 获取MessageQueue
            val messageQueue = looper.queue

            // 反射获取MessageQueue中的mIdleHandlers字段
            val idleHandlersField: Field =
                MessageQueue::class.java.getDeclaredField("mIdleHandlers")
            idleHandlersField.setAccessible(true)

            val idleHandlers = idleHandlersField.get(messageQueue) as? ArrayList<*>
            val array = idleHandlers?.toArray()

            // 打印所有的IdleHandlers
            if (array != null) {
                for (idleHandler in array) {
                    Log.d("MsgMonitor", idleHandler.toString())
                }
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }
}

参考

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注