小tip
首先说一下昨天有篇文章被举报了,很多人对此表示关心,这里非常感谢,其实昨天的文章是一篇推广文。
说一些题外话,对于推广文,很多人看到之后,会非常生气的开始留言吐槽。其实我希望的是大家可以谅解,作为一个日推的公众号,我每天会花费大量的时间去阅读、编辑和推送文章,以及回答部分日常文章中的问题。
说实在的,我也是个打工的,我也希望能够靠自己的时间挣一些收入,这些收入的主要途径其实就是推广文。
首先我会控制推广文的质量,保证其一定是行业相关的(一般是IT教程类、招聘类、职业生涯相关的);其次这类文章,不会传递一些虚假的信息,也不会对你的生活造成任何的影响,很多时候你还能从中捕获一些行业相关的信息;最后一年我推送200多篇文章,其中推广文的比例非常非常低,并且每推一次我还要承受被吐槽的风险。
这类收入其实并不多,但能够作为支撑我持续运营的动力,望理解。
ok,说完了,下面给大家带来一篇原创技术文章~~
在做app性能优化的时候,大家都希望能够写出丝滑的UI界面,以前写过一篇博客,主要是基于Google当时发布的性能优化典范,主要提供一些UI优化性能示例:
Android UI性能优化实战 识别绘制中的性能问题
http://blog.csdn.net/lmj623565791/article/details/45556391/
实际上,由于各种机型的配置不同、代码迭代历史悠久,代码中可能会存在很多在UI线程耗时的操作,所以我们希望有一套简单检测机制,帮助我们定位耗时发生的位置。
本篇博客主要描述如何检测应用在UI线程的卡顿,目前已经有两种比较典型方式来检测了:
利用UI线程Looper打印的日志
利用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种方案原理都比较简单,接下来将逐个介绍。
大家都知道在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。
用法很简单,在Application的onCreate中调用:
BlockDetectByPrinter.start();
即可。
然后我们在Activity里面,点击一个按钮,让睡眠2s,测试下:
运行点击时,会打印出log:
会打印出耗时相关代码的信息,然后可以通过该log定位到耗时的地方。
大致代码如下:
第一次的时候开始检测,如果大于阈值则输出相关堆栈信息,否则则移除。
使用方式和上述一致。
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
优秀人才不缺工作机会,只缺适合自己的好机会。但是他们往往没有精力从海量机会中找到最适合的那个。
100offer 会对平台上的人才和企业进行严格筛选,让「最好的人才」和「最好的公司」相遇。
扫描下方二维码,注册 100offer,谈谈你对下一份工作的期待。一周内,收到 5-10 个满足你要求的好机会!
如果你有想学习的文章直接留言,我会整理征稿。如果你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可。
欢迎长按下图->识别图中二维码或者扫一扫关注我的公众号: