toppic
当前位置: 首页> 玄幻小说> Android UI性能优化 检测应用中的UI卡顿

Android UI性能优化 检测应用中的UI卡顿

2022-05-07 16:07:06

小tip


首先说一下昨天有篇文章被举报了,很多人对此表示关心,这里非常感谢,其实昨天的文章是一篇推广文。


说一些题外话,对于推广文,很多人看到之后,会非常生气的开始留言吐槽。其实我希望的是大家可以谅解,作为一个日推的公众号,我每天会花费大量的时间去阅读、编辑和推送文章,以及回答部分日常文章中的问题。


说实在的,我也是个打工的,我也希望能够靠自己的时间挣一些收入,这些收入的主要途径其实就是推广文。


首先我会控制推广文的质量,保证其一定是行业相关的(一般是IT教程类、招聘类、职业生涯相关的);其次这类文章,不会传递一些虚假的信息,也不会对你的生活造成任何的影响,很多时候你还能从中捕获一些行业相关的信息;最后一年我推送200多篇文章,其中推广文的比例非常非常低,并且每推一次我还要承受被吐槽的风险。


这类收入其实并不多,但能够作为支撑我持续运营的动力,望理解。


ok,说完了,下面给大家带来一篇原创技术文章~~


1
概述


在做app性能优化的时候,大家都希望能够写出丝滑的UI界面,以前写过一篇博客,主要是基于Google当时发布的性能优化典范,主要提供一些UI优化性能示例:

  • Android UI性能优化实战 识别绘制中的性能问题

    http://blog.csdn.net/lmj623565791/article/details/45556391/


实际上,由于各种机型的配置不同、代码迭代历史悠久,代码中可能会存在很多在UI线程耗时的操作,所以我们希望有一套简单检测机制,帮助我们定位耗时发生的位置。


本篇博客主要描述如何检测应用在UI线程的卡顿,目前已经有两种比较典型方式来检测了:

  1. 利用UI线程Looper打印的日志

  2. 利用Choreographer


两种方式都有一些开源项目,例如:

  • https://github.com/markzhai/AndroidPerformanceMonitor [方式1]

  • https://github.com/wasabeef/Takt [方式2]

  • https://github.com/friendlyrobotnyc/TinyDancer [方式2]


其实编写本篇文章,主要是因为发现一个还比较有意思的方案,该方法的灵感来源于一篇给我微信投稿的项目:

  • https://github.com/android-notes/Cockroach


该项目主要用于捕获UI线程的crash,当我看完该项目原理的时候,也可以用来作为检测卡段方案,可能还可以做一些别的事情。


所以,本文出现了3种检测UI卡顿的方案,3种方案原理都比较简单,接下来将逐个介绍。


2

利用loop()中打印的日志


(1)原理


大家都知道在Android UI线程中有个Looper,在其loop方法中会不断取出Message,调用其绑定的Handler在UI线程进行执行。


大致代码如下:



所以很多时候,我们只要有办法检测:

msg.target.dispatchMessage(msg);

此行代码的执行时间,就能够检测到部分UI线程是否有耗时操作了。


可以看到在执行此代码前后,如果设置了logging,会分别打印出>>>>> Dispatching to<<<<< Finished to这样的log。


我们可以通过计算两次log之间的时间差值,大致代码如下:



假设我们的阈值是1000ms,当我在匹配到>>>>> Dispatching时,我会在1000ms毫秒后执行一个任务(打印出UI线程的堆栈信息,会在非UI线程中进行);


正常情况下,肯定是低于1000ms执行完成的,所以当我匹配到<<<<< Finished,会移除该任务。


大概代码如下:



我们利用了HandlerThread这个类,同样利用了Looper机制,只不过在非UI线程中,如果执行耗时达到我们设置的阈值,则会执行mLogRunnable,打印出UI线程当前的堆栈信息;如果你阈值时间之内完成,则会remove掉该runnable。


(2)测试


用法很简单,在Application的onCreate中调用:

BlockDetectByPrinter.start();

即可。


然后我们在Activity里面,点击一个按钮,让睡眠2s,测试下:



运行点击时,会打印出log:




会打印出耗时相关代码的信息,然后可以通过该log定位到耗时的地方。


3

利用Choreographer



理论上来说两次回调的时间周期应该在16ms,如果超过了16ms我们则认为发生了卡顿,我们主要就是利用两次回调间的时间周期来判断:


大致代码如下:



第一次的时候开始检测,如果大于阈值则输出相关堆栈信息,否则则移除。

使用方式和上述一致。


4

 利用Looper机制


new Handler(Looper.getMainLooper())
    .post(new Runnable() {
        @Override
        public void run()
        {}
    }

该代码在UI线程中的MessageQueue中插入一个Message,最终会在loop()方法中取出并执行。


假设,我在run方法中,拿到MessageQueue,自己执行原本的Looper.loop()方法逻辑,那么后续的UI线程的Message就会将直接让我们处理,这样我们就可以做一些事情:



其实很简单,将Looper.loop()里面本身的代码直接copy来了这里。当这个消息被处理后,后续的消息都将会在这里进行处理。


中间有变量和方法需要反射来调用,不过不影响查看msg.getTarget().dispatchMessage(msg);执行时间,但是就不要在线上使用这种方式了。


不过该方式和以上两个方案对比,并无优势,不过这个思路挺有意思的。

使用方式和上述一致。


最后,可以考虑将卡顿日志输出到文件,慢慢分析;可以结合上述原理以及自己需求开发做一个合适的方案,也可以参考已有开源方案。


参考

  • https://github.com/markzhai/AndroidPerformanceMonitor

  • https://github.com/wasabeef/Takt

  • https://github.com/friendlyrobotnyc/TinyDancer

ZZS

优秀人才不缺工作机会,只缺适合自己的好机会。但是他们往往没有精力从海量机会中找到最适合的那个。

100offer 会对平台上的人才和企业进行严格筛选,让「最好的人才」和「最好的公司」相遇。

扫描下方二维码,注册 100offer,谈谈你对下一份工作的期待。一周内,收到 5-10 个满足你要求的好机会!



如果你有想学习的文章直接留言,我会整理征稿。如果你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可。


欢迎长按下图->识别图中二维码或者扫一扫关注我的公众号:


友情链接