Android消息循环机制(二)Looper性能检测的缺陷

这一节介绍一个消息循环的特例。
上一节简单介绍了Android的Handler消息循环机制,大致基本就是

1、MessageQueue.next() // 没有消息就阻塞这里,有消息就取出消息
2、拿到Message返回给Looper.loop的调用处,处理消息。
3、处理结束又回到1

那现在有个疑问,有没有可能存在一段代码在主线程执行,但是没有构造Message的情况?答案其实是有。

什么情况下会出现没有构造Message但是被主线程执行的呢?

Java层代码,非常少从Looper.loop()-->>MessageQueue.next()一路把源码看一遍会发现应该不会出现这种情况。Java层代码执行结束会跑到nativePollonce方法,那有没有可能,nativePollonce方法执行后没有执行

nativePollOnce(ptr, nextPollTimeoutMillis);
 synchronized (this) {
     // Try to retrieve the next message.  Return if found.
    final long now = SystemClock.uptimeMillis();

而是去执行其他Java方法了?从代码实现上肯定是可以的。那实际有没有呢?其实是有。创建一个HelloWorld的App,不需要编写任何代码把App跑起来,然后进入debug模式。在nativePollOnce和final long now = SystemClock.uptimeMillis();打两个断点。dump堆栈数据,会发现这时候代码是跑道nativePollOnce里了。收到新Message后这一行代码会结束阻塞跑到final long now = SystemClock.uptimeMillis();来。也就是说这时候代码通常应该是主线程收到消息直接跑到第二个断点来。
关键地方来了,这时候你在屏幕上滑动下,看下有没有进入断点?确实会进入,打印这个Message消息会发现是绘制UI的Message并不是触摸事件的Message。
这时候我们把代码改造下,去MainActivity里复写方法dispatchTouchEvent然后在这个方法打印日志,并添加断点,这时候再次打开App,安静状态进入debug模式。这时候触摸下界面,神器的事情发生了,App没有进入final long now = SystemClock.uptimeMillis();这个断点,而是直接跑到dispatchTouchEvent了。这是为什么?我们打印堆栈会发现

java.lang.Thread.State: RUNNABLE
      at com.aesean.handler.MainActivity.dispatchTouchEvent(MainActivity.java:61)
      at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:71)
      at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:375)
      at android.view.View.dispatchPointerEvent(View.java:10243)
      at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4438)
      at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4306)
      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
      at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
      at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
      at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3999)
      at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
      at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4056)
      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
      at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)
      at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)
      at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)
      at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)
      at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6246)
      at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6220)
      at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6181)
      at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6349)
      at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
      at android.os.MessageQueue.nativePollOnce(MessageQueue.java:-1)
      at android.os.MessageQueue.next(MessageQueue.java:323)
      at android.os.Looper.loop(Looper.java:136)
      at android.app.ActivityThread.main(ActivityThread.java:6119)
      at java.lang.reflect.Method.invoke(Method.java:-1)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

nativePollOnce直接调用了android.view.InputEventReceiver.dispatchInputEvent。跟到这个方法

    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

注释写着Called from native code.真相大白了。native直接调用了其他方法,没有返回到调用地方MessageQueue.next执行。至于为什么会这样,这种情况下的消息处理机制可以参考:http://blog.csdn.net/luoshengyang/article/details/6882903 或者请自行搜索:Android键盘消息处理机制。

AndroidPerformanceMonitor(BlockCanary)的缺陷

AndroidPerformanceMonitor本身是个很优秀的框架,封装的很完整。但是只要是通过监控Handler消息做的性能监控都会有不能处理touchEvent监控的缺陷。原因很简单,因为touch事件的dispatch根本没有Message,也就更没有dispatchMessage了。大家可以尝试在Activity添加下面的代码,会发现这个5秒的卡顿是不会被检测到的。

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return super.onTouchEvent(event);
    }

其实对于性能监控Android已经提供了TraceView来检测App流畅程度,在Android的Sdk里你到处可见
这里的Trace.traceBegin和Trace.traceEnd就是做打点检测性能的。

  if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
      Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
  }
  try {
      msg.target.dispatchMessage(msg);
  } finally {
     if (traceTag != 0) {
      Trace.traceEnd(traceTag);
      }
  }

包括touchEvent也有打点。只不过个人觉得TraveView追踪出来的数据虽然非常详细,但是用起来其实很不方便。关于TraceView如何使用请自行Google。

Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,mPendingInputEventCount);
......
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",q.mEvent.getSequenceNumber());
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容