App性能优化之稳定性优化;十分钟带你了解Crash治理

NullPointerException空指针异常

1、NullPointerException是我们遇到最频繁的,造成这种Crash一般有两种情况:

  • 对象本身没有进行初始化就进行操作。
  • 对象已经初始化过,但是被回收或者手动置为null,然后对其进行操作。

2、两种情况对应的解决方法:

针对第一种情况导致的原因有很多,可能是开发人员的失误、API返回数据解析异常、进程被杀死后静态变量没初始化导致,我们可以做的有:

  • (1)对可能为空的对象做判空处理。
  • (2)养成使用@NonNull和@Nullable注解的习惯。
  • (3)尽量不使用静态变量,万不得已使用SharedPreferences来存储。
  • (4)考虑使用Kotlin语言。

针对第二种情况大部分是由于Activity/Fragment销毁或被移除后,在Message、Runnable、网络等回调中执行了一些代码导致的,我们可以做的有:

  • (1)Message、Runnable回调时,判断Activity/Fragment是否销毁或被移除;加try-catch保护;Activity/Fragment销毁时移除所有已发送的Runnable。
  • (2)封装LifecycleMessage/Runnable基础组件,并自定义Lint检查,提示使用封装好的基础组件。
  • (3)在BaseActivity、BaseFragment的onDestory()里把当前Activity所发的所有请求取消掉。

IndexOutOfBoundsException角标越界异常

这类Crash常见于对ListView的操作和多线程下对容器的操作。

1、针对ListView中造成的IndexOutOfBoundsException,经常是因为外部也持有了Adapter里数据的引用(如在Adapter的构造函数里直接赋值),这时如果外部引用对数据更改了,但没有及时调用notifyDataSetChanged(),则有可能造成Crash,对此我们封装了一个BaseAdapter,数据统一由Adapter自己维护通知, 同时也极大的避免了The content of the adapter has changed but ListView did not receive a notification,这两类Crash目前得到了统一的解决。

2、很多容器是线程不安全的,所以如果在多线程下对其操作就容易引发IndexOutOfBoundsException。常用的如JDK里的ArrayList和Android里的SparseArray、ArrayMap,同时也要注意有一些类的内部实现也是用的线程不安全的容器,如Bundle里用的就是ArrayMap。

(二) 系统级Crash治理/特定机型的崩溃治理

众所周知,Android的机型众多,碎片化严重,各个硬件厂商可能会定制自己的ROM,更改系统方法,导致特定机型的崩溃。发现这类Crash,主要靠云测平台配合自动化测试,以及线上监控,这种情况下的Crash堆栈信息很难直接定位问题。下面是常见的解决思路:

1、尝试找到造成Crash的可疑代码,看是否有特异的API或者调用方式不当导致的,尝试修改代码逻辑来进行规避。

2、通过Hook来解决,Hook分为Java Hook和Native Hook: Java Hook主要靠反射或者动态代理来更改相应API的行为,需要尝试找到可以Hook的点,一般Hook的点多为静态变量,同时需要注意Android不同版本的API,类名、方法名和成员变量名都可能不一样,所以要做好兼容工作; Native Hook原理上是用更改后方法把旧方法在内存地址上进行替换,需要考虑到Dalvik和ART的差异;相对来说Native Hook的兼容性更差一点,所以用Native Hook的时候需要配合降级策略。

3、如果通过前两种方式都无法解决的话,我们只能尝试反编译ROM,寻找解决的办法。

(三) OOM

OOM是OutOfMemoryError的简称,在常见的Crash疑难排行榜上,OOM绝对可以名列前茅并且经久不衰。因为它发生时的Crash堆栈信息往往不是导致问题的根本原因,而只是压死骆驼的最后一根稻草。

导致OOM的原因大部分如下:

1、内存泄漏,大量无用对象没有被及时回收导致后续申请内存失败。

2、大内存对象过多,最常见的大对象就是Bitmap,几个大图同时加载很容易触发OOM。

进行内存优化方案主要包括:

  1. 避免内存泄露
  2. 避免内存抖动:避免频繁创建大量、临时的、小的局部对象
  3. 图片Bitmap相关:释放资源、适配屏幕、解码方式、图片缓存
  4. 提高代码质量 & 减少代码数量
  5. 日常不正确使用:ListView的缓存复用、尽量少用多进程、依赖注入框架等

(四) 依赖库的问题(第三方SDK或者服务的问题)

Android App经常会依赖很多AAR,每个AAR可能有多个版本,打包时Gradle会根据规则确定使用的最终版本号(默认选择最高版本或者强制指定的版本),而其他版本的AAR将被丢弃。如果互相依赖的AAR中有不兼容的版本,存在的问题在打包时是不能发现的,只有在相关代码执行时才会出现,会造成NoClassDefFoundError、NoSuchFieldError、NoSuchMethodError等异常。

A和B两个业务库都依赖了map.aar,一个是1.0版本,一个是2.0版本,默认最终打进APK的只有map.aar 2.0版本,这时如果A库里用到的map库里的某个类/方法,但在2.0版本中被删除了,运行时就可能发生异常,虽然SDK在升级时会尽量做到向下兼容,但很多时候尤其是第三方SDK是没法得到保证的。

在这问题在HXB5.0 App里出现过,梆梆加固升级和蚂蚁金服SDK出现兼容问题,导致App闪退。也不明白为什么要用梆梆加固,对APK加固有用吗?我觉得是更是对安全问题的一个甩锅,哈哈哈。

八、Crash的预防实践&止损

(一) 预防实践

单纯的靠约定或规范去减少Crash的发生是不现实的。约定和规范受限于组织架构和具体执行的个人,很容易被忽略,只有靠工程架构和工具才能保证Crash的预防长久的执行下去。

1、工程架构对Crash率的影响

  • 业务模块的划分
  • 页面跳转路由统一处理页面跳转
  • 网络层统一处理API脏数据

2、大图监控

3、Lint检查

4、资源重复检查

5、高效的监控流程

(二) 止损

尽管我们在前面做了那么多,但是Crash还是无法避免的,例如:在灰度阶段因为量级不够,有些Crash没有被暴露出来;又或者某些功能客户端比后台更早上线,而这些功能在灰度阶段没有被覆盖到;这些情况下,如果出现问题就需要考虑如何止损了。

问题发生时首先需要评估重要性,如果问题不是很严重而且修复成本较高可以考虑在下个版本再修复,相反如果问题比较严重,对用户体验或下单有影响时就必须要修复。修复时首先考虑业务降级,主要看该部分异常的业务是否有兜底或者A/B策略,这样是最稳妥也是最有效的方式。

如果业务不能降级就需要考虑热修复了,如果问题发生在热修复无法覆盖的场景,就只能强制用户升级。强制升级因为覆盖周期长,同时影响用户的体验,只在万不得已的情况下才会使用。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

设计模式学习笔记

设计模式系列学习视频

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

…(img-SscB7FuD-1712251531545)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值