赞
踩
App Crash对于用户来讲是一种最糟糕的体验,它会导致流程中断、app口碑变差、app卸载、用户流失、订单流失等。
常见Crash的处理方式:
• 根据Crash统计平台的堆栈,用户日志,操作路径定位和解决。
• 寻找共性,机型、品牌、系统版本、所在页面、用户操作等辅助解决问题。
• 复现场景,能够复现通常就很容易解决,可以线下复现或者云真机复现。
• 对crash率较高的模块进行业务梳理,排查,重构等。
• 与第三方sdk沟通升级解决问题,修改SDK的使用方式。
1.预防Crash
对于稳定性来说,如果App已经到了线上才发现异常,那其实已经造成了损失,所以,对于稳定性的优化,其重点在于预防。Gerrit 是一个免费、开放源代码的代码审查软件,使用网页界面。我们公司大多数项目都使用Gerrit进行CodeReview,从项目开发阶段就会审查各种潜在的crash或者逻辑上的问题,严格按照代码+1、+2之后才能提交入库。
2.长效保持需要科学流程
应用稳定性的建设过程是一个细活,所以很容易出现这个版本优化好了,但是在接下来的版本中如果我们不管它,它就会发生持续恶化的情况,因此,我们必须从项目研发的每一个流程入手,建立科学完善的相关规范,才能保证长效的优化效果。在每个版本都要追踪崩溃监测平台,将对应的崩溃分发给对应业务的开发人员处理。
性能指标 | 优秀值 | 及格值 | 极差值 | 行业参考值 |
---|---|---|---|---|
崩溃率(%) | <=0.1 | 0.6 | >=1 | 0.5 |
那么,我们App的Crash率降低多少才能算是一个正常水平或优秀的水平呢?
Java与Native的总崩溃率必须在千分之二以下。
Crash率万分位为优秀:需要注意90%的Crash都是比较容易解决的,但是要解决最后的10%需要付出巨大的努力。
如果应用发生了Crash,我们应该尽可能还原Crash现场。因此,我们需要全面地采集应用发生Crash时的相关信息,如下所示:
Java中的Thread定义了一个接口: UncaughtExceptionHandler ;用于处理未捕获的异常导致线程的终止(注意:catch了的是捕获不到的),当我们的应用crash的时候,就会走 UncaughtExceptionHandler.uncaughtException ,在该方法中可以获取到异常的信息,我们通过 Thread.setDefaultUncaughtExceptionHandler 该方法来设置线程的默认异常处理器,我们可以将异常信息保存到本地或者是上传到服务器,方便我们快速的定位问题。
class MyExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) { File file = dealException(thread, throwable); //上传服务器 ... } /*** 导出异常信息到SD卡 ** @param e */ private File dealException(Thread thread, Throwable throwable) { String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); File crashFolder = new File(mContext.getExternalCacheDir().getAbsoluteFile(), CrashMonitor.DEFAULT_JAVA_CRASH_FOLDER_NAME); if (!crashFolder.exists()) { crashFolder.mkdirs(); } File crashFile = new File(crashFolder, time + FILE_NAME_SUFFIX); try { // 往文件中写入数据 PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(crashFile))); pw.println(time); pw.println(thread); pw.println(getPhoneInfo()); throwable.printStackTrace(pw); //将异常信息堆栈写入文件 // 写入crash堆栈 pw.close(); } catch (IOException ex) { ex.printStackTrace(); } return crashFile; } private String getPhoneInfo() { PackageManager pm = mContext.getPackageManager(); PackageInfo pi = null; StringBuilder sb = new StringBuilder(); try { pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); // App版本 sb.append("App Version: "); sb.append(pi.versionName); sb.append("_"); sb.append(pi.versionCode + "\n"); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } // Android版本号 sb.append("OS Version: "); sb.append(Build.VERSION.RELEASE); sb.append("_"); sb.append(Build.VERSION.SDK_INT + "\n"); // 手机制造商 sb.append("Vendor: "); sb.append(Build.MANUFACTURER + "\n"); // 手机型号 sb.append("Model: "); sb.append(Build.MODEL + "\n"); // CPU架构 sb.append("CPU: "); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { sb.append(Arrays.toString(Build.SUPPORTED_ABIS)); } else { sb.append(Build.CPU_ABI); } return sb.toString(); } }
然后在application中设置自定义的UncaughtExceptionHandler。
Thread.setDefaultUncaughtExceptionHandler(MyExceptionHandler())
1.在主线程中,进行了触屏点击滑动等操作,在5秒之内对该事件没有响应,就会导致ANR(例如,按键按下,屏幕触摸)
2.BroadcastReceiver在10秒内没有执行完毕
3.service是20秒
根本原因是在主线程进行了耗时操作,导致后面的消息无法处理,比如:
1.耗时的网络访问
2.大量的数据读写
3.数据库操作
4.在主线程中调用thread的join()方法、sleep()方法、wait()方法
5.其他线程持有锁,导致主线程等待超时
6.子线程终止或崩溃导致主线程一直等待
解决的主要办法就是开启子线程去处理这些耗时操作。修改主线程去等待子线程的锁。
1、抓取bugreport
adb shell bugreport > bugreport.txt
2、直接导出/data/anr/traces.txt文件
adb pull /data/anr/traces.txt trace.txt
监控方法:通过 FileOberver 监控 data/anr 文件夹下文件的变化,来确定ANR的发生。
获取信息:主线程堆栈信息+ANR信息。
由于刚监控到ANR发生时进程并没有进入ANR状态,而是先向 /data/anr/ 文件中写入进程信息,此时并不会立即获取到进程的ANR信息,需要循环等待进程进入ANR状态。
解决ANR问题,首先要做的是找到问题,线下我们可以通过ADB命令导出ANR文件进行分析,线上我们可以使用FileObserver或ANR-WatchDog保存ANR堆栈信息,然后上传到服务器。
ANR发生之后我们可以使用以下命令导出ANR文件:
adb bugreport
在关键词DALVIK THREADS和"main" prio=5 tid=1 Native下面可获取ANR的日志:
使用FileObserver监控data/anr目录,当文件状态发生改变、创建或删除时,说明当前发生了anr事件。
创建类继承FileObserver,监控data/anr文件:
class AnrFileObserver extends FileObserver { @Override public void onEvent(int event, @Nullable String path) { switch (event) { case FileObserver.ACCESS: break; case FileObserver.CREATE: break; case FileObserver.MODIFY: break; } } }
接入 bugly,根据App版本,手机型号,发生时间,次数,产生位置进行位置问题定位并解决。
ANR异常我们可分为线上监测和线下监测两个方向
线上监测主要是利用FileObserver进行ANR目录文件变化监听,以ANR-WatchDog进行补充。
FileObserver在使用过程中应注意高版本程序不可用以及预防死锁出现。
线下监测主要是在报错之后利用ADB命令将错误的日志导出并找到错误的类进行分析。
1.功能开关
首先,需要让App具备一些高级的能力,我们对于任何要上线的新功能,要加上一个功能的开关,通过配置中心下发的开关呢,来决定是否要显示新功能的入口。如果有异常情况,可以紧急关闭新功能的入口,那就可以让这个App处于可控的状态了。
2.关闭灰度发布
如果问题更严重,范围更广,可以立即关闭灰度发布版本。
3.动态修复:热修复、资源包更新
目前热修复的方案其实已经比较成熟了,我们完全可以低成本地在我们的项目中添加热修复的能力,当然,如果有些功能是由RN或WeeX来实现就更好了,那就可以通过更新资源包的方式来实现动态更新。
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。