JavaWeb程序在运行的过程中,只要还有其他线程运行,JVM虚拟机就不会关闭,进程就不会结束。但是在Android App的运行过程中,不论是主线程还是子线程,或是三方库的子线程,只要发生异常,就会引起应用崩溃。原因是JavaWeb程序中没有设置默认的线程异常处理器,而Android系统为每一个Android App进程都设置了默认的线程异常处理器。
五、Crash的影响
Crash率是衡量一个App口碑好坏的重要指标之一。如果忽略了它的存在,它就会得寸进尺,愈演愈烈,最后造成大量用户的流失,进而会带来无法估量的损失。
六、Crash治理原则
对于Crash的治理,我们尽量遵守以下三点原则:
1、由点到面。一个Crash发生了,我们不能只针对这个Crash的去解决,而要去考虑这一类Crash怎么去解决和预防。只有这样才能使得这一类Crash真正被解决。
2、异常不能随便吃掉。随意的使用try-catch,只会增加业务的分支和隐蔽真正的问题,要了解Crash的本质原因,根据本质原因去解决。catch的分支,更要根据业务场景去兜底,保证后续的流程正常。
3、预防胜于治理。当Crash发生的时候,损失已经造成了,我们再怎么治理也只是减少损失。尽可能的提前预防Crash的发生,可以将Crash消灭在萌芽阶段。
七、Crash治理分类
##(一) 常规的Crash治理
常规Crash发生的原因主要是由于开发人员编写代码不小心导致的。解决这类Crash需要由点到面,根据Crash引发的原因和业务本身,统一集中解决。常见的Crash类型包括:空节点、角标越界、类型转换异常、实体对象没有序列化、数字转换异常、Activity或Service找不到等。这类Crash是App中最为常见的Crash,也是最容易反复出现的。在获取Crash堆栈信息后,解决这类Crash一般比较简单,更多考虑的应该是如何避免。下面介绍两个我们治理的量比较大的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。
进行内存优化方案主要包括:
- 避免内存泄露
- 避免内存抖动:避免频繁创建大量、临时的、小的局部对象
- 图片Bitmap相关:释放资源、适配屏幕、解码方式、图片缓存
- 提高代码质量 & 减少代码数量
- 日常不正确使用: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、大图监控
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/b476f59f4b5bf01b75a540313752a425.jpeg)
如何成为Android高级架构师!
架构师必须具备抽象思维和分析的能力,这是你进行系统分析和系统分解的基本素质。只有具备这样的能力,架构师才能看清系统的整体,掌控全局,这也是架构师大局观的形成基础。 你如何具备这种能力呢?一是来自于经验,二是来自于学习。
架构师不仅要具备在问题领域上的经验,也需要具备在软件工程领域内的经验。也就是说,架构师必须能够准确得理解需求,然后用软件工程的思想,把需求转化和分解成可用计算机语言实现的程度。经验的积累是需要一个时间过程的,这个过程谁也帮不了你,是需要你去经历的。
但是,如果你有意识地去培养,不断吸取前人的经验的话,还是可以缩短这个周期的。这也是我整理架构师进阶此系列的始动力之一。
成为Android架构师必备知识技能
对应导图的学习笔记(由阿里P8大牛手写,我负责整理成PDF笔记)
部分内容展示
《设计思想解读开源框架》
- 目录
- 热修复设计
- 插件化框架设计
《360°全方面性能优化》
- 设计思想与代码质量优化
- 程序性能优化
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
[外链图片转存中…(img-rupvJPy0-1713294731764)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!