在低端机上,使用过traceview,systrace,handler的日志来优化性能,最有效果的是利用Handler日志来优化.最近在喜马拉雅的pad移植我们的APP,发现页面比较卡顿,但APP上很流畅.但是老板觉得其他APP在pad上比较流畅,我们的APP也能做到,所以分派下来优化性能.Android Stduio的profiler是分析卡顿耗时的利器,但是一旦开启profier,app直接卡死,没想到profiler对性能影响这么大.利用systrace,就得插桩设置section,给所有方法插装不太现实,所以这个方案也抛弃了.那么最后只剩下Handler了,打印主线程耗时的地方,因为卡顿基本和主线程有关.用过之后,是真的好用.

这个功能分为两个内容,

如何判断一个message的执行开始和结束

在Handler执行Message的源码中,有message执行的对开的开始和结束标志

  public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();// Allow overriding a threshold with a system prop. e.g.// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'final int thresholdOverride =SystemProperties.getInt("log.looper."+ Process.myUid() + "."+ Thread.currentThread().getName()+ ".slow", 0);boolean slowDeliveryDetected = false;for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {//message执行开始 ==============logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;if (thresholdOverride > 0) {slowDispatchThresholdMs = thresholdOverride;slowDeliveryThresholdMs = thresholdOverride;}final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);final boolean needStartTime = logSlowDelivery || logSlowDispatch;final boolean needEndTime = logSlowDispatch;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;final long dispatchEnd;try {msg.target.dispatchMessage(msg);dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logSlowDelivery) {if (slowDeliveryDetected) {if ((dispatchStart - msg.when) <= 10) {Slog.w(TAG, "Drained");slowDeliveryDetected = false;}} else {if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",msg)) {// Once we write a slow delivery log, suppress until the queue drains.slowDeliveryDetected = true;}}}if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);}if (logging != null) {//message执行结束 ==============logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();}}

根据这个原理,给Looper设置我们自己的logPrinter

 Looper.getMainLooper().setMessageLogging(new LogMonitorPrinter(Log.DEBUG, "Monitor"));

LogMonitorPrinter的源码如下

import android.os.Looper;
import android.util.Log;
import android.util.LogPrinter;/*** @auther:**** @date:2022/1/12*/
public class LogMonitorPrinter extends LogPrinter {static final String TAG = "LogMonitorPrinter";/*** Create a new Printer that sends to the log with the given priority* and tag.** @param priority The desired log priority:*                 {@link Log#VERBOSE Log.VERBOSE},*                 {@link Log#DEBUG Log.DEBUG},*                 {@link Log#INFO Log.INFO},*                 {@link Log#WARN Log.WARN}, or*                 {@link Log#ERROR Log.ERROR}.* @param tag      A string tag to associate with each printed log statement.*/public LogMonitorPrinter(int priority, String tag) {super(priority, tag);}@Overridepublic void println(String x) {if (x.startsWith(">>>>> Dispatching to")) {LogMonitor.getIntance().startMonitor();}if (x.startsWith("<<<<< Finished to")) {LogMonitor.getIntance().removeMonitor();}super.println(x);}}

如何获取当前message的堆栈.

这里利用一个小技巧,我们利用HandlerThread在异步线程中类似检测anr的方式,在阈值内如果打印,则移除callback,否则执行callback打印main looper的堆栈信息.

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;/*** @auther:**** @date:2022/1/14*/
public class LogMonitor {private static volatile LogMonitor intance = null;public synchronized static LogMonitor getIntance() {if (intance == null) {synchronized (LogMonitor.class) {if (intance == null) {intance = new LogMonitor();}}}return intance;}private Handler mIogHandler;private static final long TIME_BLOCK = 300L;private LogMonitor() {//Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes.HandlerThread mLogThread = new HandlerThread("log");mLogThread.start(); //Note that start() must still be called.mIogHandler = new Handler(mLogThread.getLooper());}private static Runnable mLogRunnable = () -> {StringBuilder sb = new StringBuilder();StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();for (StackTraceElement s : stackTrace) {sb.append(s.toString()).append("\n");}Log.w(LogMonitorPrinter.TAG, sb.toString());};public void startMonitor() {mIogHandler.postDelayed(mLogRunnable, TIME_BLOCK);}public void removeMonitor() {mIogHandler.removeCallbacks(mLogRunnable);}
}

实战效果,在Application的onCreate中进行设置,基本上能打印了在主线程耗时的方法.有复杂布局,有binder调用,有数据库读写,还有大bitmap处理.我们根据需要优化,启动性能提升了4s左右.还有一个额外收益,因为设置了logPrinter,Looper其他日志信息的在我们的进程中也能看到了.我们查到我们的一个接口请求的开始-->回调的返回耗时(一般接口数据返回,用Hanlder切回主线程来刷新UI)远大于实际接口的请求返回数据的耗时,是因为这个message被其他的message block了.然后我们利用Handler的

sendMessageAtTime(msg, 0)
postAtFrontOfQueue()

让我们的message排到队头,优先执行,最快刷新UI,优化我们关键的message被其他message block的现象.message的执行优先级,可以看这篇文章Android 实战中提高Handler发送消息的优先级

当然这在中高端机型上基本上不太可能出现这个情况.

利用Hander的日志优化性能相关推荐

  1. 利用预渲染解决优化性能问题IOS

    http://www.keakon.net/2011/07/26/%E5%88%A9%E7%94%A8%E9%A2%84%E6%B8%B2%E6%9F%93%E5%8A%A0%E9%80%9FiOS% ...

  2. 优化性能问题的一般方法

    本文是通过学习倪朋飞老师的<Linux性能优化实战> :分析性能问题的一般步骤 优化性能问题的一般方法 系统优化 CPU 优化 内存优化 磁盘和文件系统 I/O 优化 网络优化 应用程序优 ...

  3. 前端不哭!最新优化性能经验分享来啦 | 技术头条

    作者 | Dimitris Kiriakakis 译者 | 风车云马 编辑 | Jane 出品 | Python大本营(id:pythonnews) [导语]Angular.React.VueJS 是 ...

  4. [转帖]ASP.NET中常用的优化性能的方法

    ASP.NET中常用的优化性能的方法(转贴,Icyer收集整理) 1.       数据库访问性能优化     数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要 ...

  5. ZooKeeper的配置文件优化性能(转)

    一.前言 ZooKeeper的功能特性通过ZooKeeper配置文件来进行控制管理( zoo.cfg配置文件). ZooKeeper这样的设计其实是有它自身的原因的.通过前面对ZooKeeper的配置 ...

  6. c mysql批量插入优化_MySQL实现批量插入以优化性能的教程

    这篇文章主要介绍了MySQL实现批量插入以优化性能的教程,文中给出了运行时间来表示性能优化后的对比,需要的朋友可以参考下 对于一些数据量较大的系统,数据库面临的问题除了查询效率低下,还有就是数据入库时 ...

  7. 硬件加速下webview切换闪屏_网页渲染性能优化 —— 性能优化下

    博客 有更多精品文章哟. Composite 的优化 终于,我们到了像素管道的末尾.对于这一部分的优化策略,我们可以从为什么需要 Composited Layer(Graphics Layer)来入手 ...

  8. mysql如何优化性能优化_如何优化性能?MySQL实现批量插入以优化性能的实例详解...

    这篇文章主要介绍了MySQL实现批量插入以优化性能的教程,文中给出了运行时间来表示性能优化后的对比,需要的朋友可以参考下 对于一些数据量较大的系统,数据库面临的问题除了查询效率低下,还有就是数据入库时 ...

  9. 祖传代码如何优化性能?

    Hollis的新书限时折扣中,一本深入讲解Java基础的干货笔记! 为了新朋友能快速进入场景,再描述一遍这个项目的背景,这个项目是一个自研的Dubbo注册中心,上一张架构图 Consumer 和 Pr ...

最新文章

  1. 手撸一个JdbcTemplate,带你了解其原理
  2. Dwr 框架简单实例
  3. 在linux 下怎么查看服务器的cpu和内存的硬件信息
  4. mysqlbinlog_flashback工具体验
  5. 【模型训练】SGD的那些变种,真的比SGD强吗
  6. Pinyin4j中文字符和拼音之间的转换
  7. php对外发包引发服务器崩溃的终极解决方法分享
  8. SAP Spartacus PersistFocus Directive是采取怎样的数据结构来存储focus信息的
  9. Android XML文件中设置字体
  10. CRM系统助家具企业华丽转身
  11. miui12 android系统耗电,miui12耗电严重怎么办,miui12续航优化方法
  12. Redis Cluster 添加/删除 完整折腾步骤
  13. MAP地图报错Unable to preventDefault inside passive event listener invocation.
  14. Android开源框架源码分析:Okhttp
  15. 【Visual C++】Windows GDI贴图闪烁解决方法
  16. 论一只爬虫的自我修养11:Scrapy框架之初窥门径
  17. 机器学习在网络中的实际应用
  18. java for 死循环_一个Java For语句死循环的例子源码
  19. Failure obtaining db row lock: Lock wait timeout exceeded; try restarting transaction java定时任务
  20. 微信小程序账号注册流程

热门文章

  1. JAVA语言的介绍和特性
  2. JavaScript:base64编码与解码
  3. 法国敏捷开发与敏捷测试模式
  4. svn服务器搭建(一) VisualSVN-Server
  5. 老弟,来了?VUE+Nuxt.js+Koa+Vuex入门教程(一)仿写一个cnode网站
  6. 密码算法测试向量——AES
  7. 阿里入股新浪微博:动机与前景分析
  8. Flink实操 : DataSource操作
  9. Java题目详解——LeetCode20.有效的括号
  10. 详解,N沟道MOS管和P沟道MOS管