【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制

本文主要是介绍【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文作者:鲁可——腾讯SNG专项测试组 测试工程师

背景

承上经典随机Crash之一:线程安全

问题的模型

好几次灰度top1、top2 Crash发生场景:在很平常、频繁的使用页面,打开一个界面,马上返回,piaji,挂了,估计用户心中有千万只草泥马在奔腾,手机QQ究竟怎么呢?

找到开发童鞋,还是熟悉的对话:

  1. 请教:这个Crash能复现吗?开发答:场景就在这,就是复现不了啊

  2. 这里有个空指针,那我就加个判空

我只好去看下开发童鞋的代码,发现都有一个共性,跟handler postDelayed有关系,这里抽取出Crash代码梗概

Post一个匿名Runnable,延迟500ms

跟开发童鞋反复再三确认,mGLVideoView置空的地方只有一处,就在onDestroy()

开发童鞋一般为了解决内存泄露问题,会在onDestroy中将变量置空,以让系统回收,这么做也理所当然。跟用户反馈的情况也吻合,打开界面,立马返回,会Crash。

为了搞清这个问题的根源,需要对Android消息机制有一定了解,大家可以搜索下相关文章。

不按套路出牌,碉堡了的用户是这样的,如图所示

弱爆了的我是这样的,如图所示

那接下来的事情就好办了,寻找腾讯手速最快的人,要在500ms之内打开界面,返回,要是他都复现不了,那就真的复现不了,虽然是开个玩笑,但这确实已经不是个概率性问题了,在我们手速不够快的情况下,这类型Crash确实是复现不了,但很显然这不是解决问题的正确姿势。

解决问题的思路

事后手段:

  1. 加判空

  2. 这里给大家推荐这篇文章:

    Android handler.removeCallbacksAndMessages(null)的妙用
    http://www.snowdream.tech/2016/02/18/handler-removeCallbacksAndMessages/

好处有:非静态匿名内部类Runnable持有外部类会导致内存泄露,remove掉以较少内存泄露;消除这类空指针Crash的隐患;减少主线程消息队列的任务,还能提升点性能

然而这些都不能做到事前发现,今天我们就一起来探讨下一些事前的手段,并解密一个我申请的有利于发现同类问题的专利。

请教了做静态检查的同学,在没有任何上下文环境的情况下直接使用一个变量,这种空指针检查很难搞,我们主要从动态角度上分析。

1、 在activity onDestroy之后handler.post

监控Activity onDestroyhandler post操作,强制在onDestroy之后再post,就能100%复现这个Crash了

那首先需要寻找Activity与handler之间的联系,监控onDestroy,可以用hook或者类似LeakCanary的方式,注册ActivityLifecycleCallbacks来监听,但难点在怎么把handler postActivity onDestroy建立起联系,从开发者的角度来说,这两个模块没有联系,Activity完全不用handler也是可以的,在Activity的生命周期方法中,没有哪个需要带上handler,Activity中会不会默认隐藏着handler了?

抱着这样的疑问,我去看了下Activity的源码(以Android5.0为准)

果真Activity中会有一个mHandler

看了下这个mHandler在什么地方会被用到

只有在runOnUiThread中会被用到,但开发者自己绑定MainLooperhandler跟这个mHandler没有关系。

这种方法需要对Activity Handler两大核心模块找到一种关联,并做一种高精度的手术,限于本人能力有限,一时陷入了困境。

2、 控制消息的时机

既然没法找到Activity Handler的关联,就只好从消息机制本身着手。

刚开始我们想到的方法,把这种消息从消息队列里取出来,等待时机,然后再重新插入消息队列

那第一步就需要把这种消息取出来,我们先来看看源码是怎么做的

loop()中会通过next()获取一个消息,如果能获取到,则通过dispatchMessage()分发消息,接下来我们看看next()是怎么获取消息的

next获取了当前系统时间,若到了消息执行时间,则返回消息

这里一定会有疑问,msg.when是怎么设置的?消息是如何插入队列的?

next()从消息队列获取一个消息,无法精准到具体的消息,其实我们还可以参考removeMessages的实现,通过反射来取出消息,如果remove的时机过晚,也会导致这个消息已经被消费了,如果remove错了,导致丢消息,篓子就捅大了。总之,我们必须搞清楚消息入队列的过程。

发送消息主要有sendXXX,postXXX两大类方法,由于Runnable也会被封装成Message

其实这里面也会有个坑:Callback类型Message的what是0,大家有兴趣也可以学习下

看过post (runnable)sendMessage过程后,我画了一个postXXX、sendXXX调用关系图

根据上面的图,可以看出sendMessageDelayedsendMessageAtTime是非常重要的两个环节,我们来看下这两个方法究竟做了啥

sendMessageDelayed中会用系统开机总时间+dalayMillis,所以传入sendMessageAtTime的值是相对于系统启动的绝对值

再来看queue.enqueueMessage的过程

when赋值给了msg.when,这下能解释next()msg.when是如何得来的问题,到这里,您应该清楚了,原来插入消息队列的顺序是根据msg.when大小来插入的。

前面说到when传入的是一个绝对值,那上面为啥有when==0的判断,那什么时候when会为0呢?当把一个消息强制插入到队列首的位置,会传入0

如果我们要延迟那个消息的处理时机,只需改动这个绝对值就可以了,我们决定通过hook sendMessageDelayed,将延迟时间delayMillis改长,如果您看到这里,是不是觉得方案其实很简单?确实是的,如果我一上来就告诉您这么做,那这个问题就很简单了,其实中间也是踩了一些坑,然而知道为什么要这么做,似乎更重要,也更有趣。

到此,您已经清楚Android是如何插入消息的了,您要是愿意,完全可以把全部消息hook住了,随意改uptimeMillis,那您已经掌握了玩弄消息顺序于股掌之中的技术。

问题的解决方案

最终综合安全性、稳定性等方面的考虑,我们采用了将delayMillis时间改长的方案

  1. 考虑到主线程做了很多事情,比如需处理绘制UI等一些系统消息,而开发者一般把延时操作都放在了Runnable里,这里我们只延迟Runnable经过封装的消息,并根据调用堆栈做了过滤

  2. 考虑到这种Crash容易发生在post短时间内,如果开发者本来设置的延迟时间就比较大,如果再加大延迟,会让消息得不到及时处理,所以我们对需要加大延迟的时间做了阈值判断

最终实现的流程图如下图所示:

因此,这个专利水到渠成:一种延迟消息分发模拟Crash的方法

最终要达到的效果下图所示:

众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。延迟一个小时,我完全可以出去吃个饭、遛个弯,再回来复现这个Crash了。

问:跟当前主线程卡顿监控方案是否有冲突?

答:主线程卡顿监控主要是计算dispatchMessageDispatchingFinished之间的耗时,我们对dispatchMessage没做任何手脚,只是延迟了消息的处理时机。

问:会不会造成卡顿?

答:UI上的不流畅主要是掉帧,每个消息具体耗时多少,还是取决于消息本身在做什么,我们跟开发者自己把delayMillis改长并没什么区别。

效果

延迟消息分发SDK已加入NewMonkey随身版挑战者模式中,能做到无场景延迟Runnable类型消息的分发,功能上线短短1天内,就发现了Android QQ 4个Crash,都得到了开发同学的迅速fix。

由于本人能力、精力有限,对Android消息机制远未啃透,若有纰漏,欢迎斧正,对其他平台的消息机制更是一窍不通,若对您有所启发,深感荣幸。

道高一尺魔高一丈,在降Crash率上,依旧任重而道远。


更多精彩内容欢迎关注腾讯 Bugly的微信公众账号:

腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

这篇关于【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/839204

相关文章

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

【Tools】大模型中的自注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 自注意力机制(Self-Attention)是一种在Transformer等大模型中经常使用的注意力机制。该机制通过对输入序列中的每个元素计算与其他元素之间的相似性,

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分