线上问题排查
怎么排查
- 日志打点怕打太多也怕太少,担心出现问题没有足够丰富的信息去定位分析问题。应该打多少日志,如何去打日志并没有一个非常严格的准则,这需要整个团队在长期实践中慢慢去摸索。在最开始的时候,可能大家都不重视也不愿意去增加关键代码的日志,但是当我们通过日志平台解决了一些疑难问题以后,团队内部的成功案例越来越多的时候,这种习惯也就慢慢建立起来了。
- 使用 Mars 的 xlog,Java 实现写日志,GC 频繁,而 C 实现并不会出现这种情况,因为它不会占用 Java 的堆内存。
- 使用阿里云日志采集服务
- 俩种方式上报日志:push 上报,主动上报(在用户出现奔溃,反馈问题时主动上报日志(可以重启了上报))
- 正因为反复“痛过”,才会有了微信的用户日志和点击流平台,才会有美团的 Logan 和 Homles(看看) 统一日志系统。所谓团队的“提质增效”,就是寻找团队中这些痛点,思考如何去改进。无论是流程的自动化,还是开发新的工具、新的平台,都是朝着这个目标前进。
永不崩溃
CrashProtectManager.getInstance (this). init ();
可以重写 thread 的 UncaughtExceptionHandler,在这里进行全局捕获异常,正常情况崩溃了 APP 会杀死进程,捕获了就不会了。
点击崩溃的地方会没有响应,这是因为发生异常后,looper 退出了,而 Android 打开关闭页面、服务的开启绑定、取消等很多操作都是通过 looper 的,可以在捕获的地方发生异常后调用 Looper.loop (); 这样就不会没有响应了。可以写入文件、或者发送给服务端
Android 混淆后还怎么看错误
保留关键信息:在混淆配置文件(通常是 proguard-rules.pro
)中,您可以添加规则来保留某些类、方法或字段的名称,以便在混淆后的应用程序中仍然能够识别它们。例如:
1 |
|
注解混淆
不想混淆的类需要一个个添加到 proguard-rules. pro (或 proguard. cfg) 中吗?这样会导致 proguard 配置文件变得杂乱无章,同时需要团队所有成员对其语法有所了解。
解决方法1:
1 |
|
NotProguard 是个编译时注解,不会对运行时性能有任何影响。可修饰类、方法、构造函数、属性。
然后在 Proguard 配置文件中过滤被这个注解修饰的元素,表示不混淆被 NotProguard 修饰的类、属性和方法。
1 |
|
解决方法2:
1 |
|
问题不是自己 app,怎么解决
bugly 报的很多问题不是自己 app 的类,怎么解决
第三方库或依赖项:Bugly 可以报告与您的应用程序相关的第三方库或依赖项中出现的问题。这可能是由于库本身的 bug、版本不兼容或配置错误等原因引起的。在这种情况下,您可以尝试更新相关的库版本,查看是否已经发布了修复此问题的更新版本。另外,您还可以尝试搜索相关问题的解决方案或在开发者社区中提问以获得帮助。
系统组件或操作系统问题:有时 Bugly 报告的问题可能涉及到 Android 系统组件或操作系统本身的问题。这可能是由于特定设备、Android 版本或其他环境因素导致的。在这种情况下,您可以尝试查看 Bugly 提供的详细信息,例如堆栈跟踪、设备信息等,以了解问题发生的背景。然后,您可以尝试在 Bugly 或其他社区中搜索相关问题,看是否有其他开发者遇到过类似问题,并找到解决方案或工作回避方法。
混淆和符号化:如果您在应用程序中使用了代码混淆(如 ProGuard)并启用了符号化配置,Bugly 报告的问题可能显示的是混淆后的类名或方法名。在这种情况下,您可以尝试使用符号化映射文件(mapping file)将混淆后的类名还原为原始的类名,以便更好地理解问题出现的位置和上下文。您可以通过在 Bugly 控制台中上传符号化映射文件来实现这一点。
符号化映射文件
将应用程序的崩溃堆栈信息转换为可读形式的文件。它包含了应用程序的符号表(Symbol Table)信息,将编译后的函数和变量名映射回原始的源代码符号,使得崩溃日志更易于理解和分析。
符号化映射文件通常在应用程序构建过程中生成,并与应用程序的发布版本一起打包。在 Android 开发中,常用的构建工具如 ProGuard 或 R8 可以生成符号化映射文件。该文件通常具有 “. mapping” 或 “. txt” 的扩展名。
需要注意的是,符号化映射文件包含敏感信息,如函数名和行号等。因此,为了保护应用程序的安全,符号化映射文件应妥善管理,并不应该随意公开或共享。
Mapping 文件的默认位置为 app/build/outputs/mapping/release/mapping.txt
线上问题
RecyclerView IndexOutOfBoundsException
java. lang. IndexOutOfBoundsException: Inconsistency detected.
重现的方法是:使用 RecyclerView 加官方下拉刷新的时候,如果绑定的 List 对象在更新数据之前进行了 clear,而这时用户紧接着迅速上滑 RV,就会造成崩溃,而且异常不会报到你的代码上,属于 RV 内部错误。初次猜测是,当你 clear 了 list 之后,这时迅速上滑,而新数据还没到来,导致 RV 要更新加载下面的 Item 时候,找不到数据源了,造成 crash.
内外数据不一致
解决方法: 就是在刷新,也就是 clear 的同时,让 RecyclerView 暂时不能够滑动,之后再允许滑动即可。代码就是在 RecyclerView 初始化的时候加上是否在刷新进而拦截手势:
1 |
|
有多种解决方式
https://www.jianshu.com/p/073031d4b9e0
https://stackoverflow.com/questions/30220771/recyclerview-inconsistency-detected-invalid-item-position
复现步骤是,对有数据的列表刷新操作,在还未刷出数据的时候,不断的滑动,Crash。很容易理解,也就是在 Recyclerview 滑动的时候,执行 notifyDataSetChanged () 导致的。
IllegalStateException : Can not perform this action after onSaveInstanceState
Fragment 在显示或者隐藏,移除是出现 Can not perform this action after onSaveInstanceState 解决办法:onSaveInstanceState 方法是在该 Activity 即将被销毁前调用,来保存 Activity 数据的,如果在保存玩状态后再给它添加 Fragment 就会出错。解决办法就是把 commit()方法替换成 commitAllowingStateLoss ()
commit () 和 commitAllowingStateLoss () 在实现上唯一的不同就是当你调用 commit () 的时候, FragmentManger 会检查是否已经存储了它自己的状态, 如果已经存了, 就抛出 IllegalStateException.
1 |
|
BadTokenException
异常的原因都有很多种
UI 线程发生阻塞,导致 TN.show () 没有及时执行,当 NotificationManager 的检测超时后便会删除 WMS 中的该 token,即造成 token 失效。
Google 在 API26中修复了这个问题,即增加了 try-catch:
因此对于8.0之前的我们也需要做相同的处理。DToast 是通过反射完成这个动作,具体看下方实现:
对 dispatchMessage try-catch
1 |
|
另一种 BadTokenException
- 不能在 Activity 没有完全显示时显示 PopupWindow 和 Dialog。例如在 activity 的 onCreate 方法里面调用 popupwindow 的 show 方法,有可能由于 activity 没有完全初始化导致程序异常(android. view. WindowManager$BadTokenException: Unable to add window – token null is not valid),如果非要在一进 activity 就显示 popupwindow,用 handler. post、View. postDelay 来处理。
或者获取宽高获取不到
内部执行是通过 handler 去执行的, View. post 调用时会把 Runnable 保存到一个缓存数组中,等到 View 加载到 Window 时会调用 dispatchAttachedToWindow 方法,然后通过 handler 执行 Runnable 方法,就可以获取到他的宽高。
在弹窗前先判断好当前界面是已经否被结束掉了: if (! isFinishing ()) { mDialog.show (); }
TimeoutException
在 GC 时,为了减少应用程序的停顿,会启动四个 GC 相关的守护线程,FinalizerDaemon:析构守护线程。对于重写了成员函数 finalize 的对象,它们被 GC 决定回收时,并没有马上被回收,而是被放入到一个队列中,等待 FinalizerDaemon 守护线程去调用它们的成员函数 finalize,然后再被回收。一旦检测到执行成员函数 finalize 时超出一定的时间,那么就会退出 VM。我们可以理解为 GC 超时了。这个时间默认为10s
解决方法:通过反射最终将 FinalizerWatchdogDaemon 中的 thread 置空,这样也就不会执行此线程,所以不会再有超时异常发生
1 |
|
许多 Android 开发者可能经常遇到这样的情况:测试的时候好好的,一上线,各种系统的 crash 就报上来了,而且很多是偶现的,比如:
1 |
|
很多情况下,这些异常崩溃并不是由 APP 导致的,而且堆栈中也没有半点 APP 的影子,就拿 DeadObjectException 来说,一般都是由于提供远程服务的进程挂掉导致,如果是 APP 代码逻辑的问题,很容易就能在堆栈中发现,那如果是因为系统导致的崩溃,我们难道就无能为力了?
https://booster.johnsonlee.io/feature/bugfix/prevent-crash-from-system-bug.html#莫名其妙的崩溃
https 页面加载 http 图片不显示
https 页面加载 http 图片或者 http 页面加载 https 图片时图片显示不出来,原因是因为在 Android 5.0开始 WebView 默认不允许加载 http 与 https 混合页面,当然最好还是不要混合,保持统一。
解决办法:
1 |
|
SIGBUS 相关问题
so 包就是 c++的库,类似 jar 包
主要集中在集成的极光推送
为了瘦身我们的 apk 文件,我只添加了 armeabi-v7a 架构的相关 so 文件。结果在 oppo 的这些手机上没有兼容,或者说更加的严格,导致了未对齐的数据访问。为什么这么说,因为后来有观察再升级极光的 sdk 后,发现这类问题有所下降。当然如果你直接添加上 arm64-v8a,则不会有这个问题。