稳定性优化

稳定性中两个常见场景:Crash和ANR

提高代码质量

代码审查

  1. 何时审查
    分两个方面:一是这个模块是否需要审查,明确代码审查的必要性,二是在开发阶段的哪个时间点代码审查,确定审查合理时间点。

一般审查:底层公共模块、重大特性业务代码、与其他模块有耦合、新手、应用即将发布前的紧急修改。

  1. 谁来审查

代码审查分为三种方式

  • 团队审查:底层通用模块
  • 模块负责人审查:某些模块化的功能和业务
  • 结对审查:两人结对互相审查
  1. 审查内容

审查的流程:先审查设计实现思路,然后审查设计模式,接着审查形成的骨干代码,然后审查完成的代码。

审查内容:

  • 实现思路和设计思想
  • 代码设计
  • 设计逻辑
  • 代码风格
  • 需求理解

代码静态扫描工具

4种常用Java代码分析工具对比如下

image.png

  1. Checkstyle:通过对代码编码格式、命名约定、Javadoc、类she j设计等方面进行代码规范和风格的检查。
  2. FindBugs:通过检测类文件或JAR文件,将字节码与一组缺陷模式进行对比从而发现代码缺陷,完成静态代码分析。
  3. PMD:通过其内置的编码规则对Java代码进行静态检查,主要检查潜在的bug、未使用的代码、重复的代码、循环体创建新对象等问题。
  4. Android Lint:除了代码缺陷,还能检测代码布局的合理性。

Crash监控

Android应用中发生的crash有两种类型,Java层的Crash和Native层的Crash

Java层的Crash监控

Android中,Java虚拟机为每个进程都设置类一个默认的UncaughtExeptionHandler,用于处理本进程中未被try catch的Exception。因此只有实现UncaughtExeptionHandler接口,并进程启动时调用Thread.setDefaultUncaughtExceptionHandler(...)传入自定义的UncaughtExeptionHandler,发生未捕获异常时就会回调uncaughtException(Thread thread, Throwable ex) 方法。

demo

public class ABLCrashHandler implements UncaughtExceptionHandler {public static final String TAG = "ABLCrashHandler";// ABLCrashHandler 实例private static ABLCrashHandler INSTANCE = null;// 程序的 Context 对象private Context mContext;// 系统默认的 UncaughtException 处理类private UncaughtExceptionHandler mDefaultHandler;// 用来存储设备信息和异常信息private Map<String, String> infos = new HashMap<String, String>();//保证只有一个 ABLCrashHandler 实例private ABLCrashHandler(Context context) {this.init(context);}//获取 ABLCrashHandler 实例 ,单例模式public static ABLCrashHandler getInstance(Context context) {if (INSTANCE == null) {INSTANCE = new ABLCrashHandler(context);}return INSTANCE;}/*** 初始化** @param context*/public void init(Context context) {mContext = context;// 获取系统默认的 UncaughtException 处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 设置该 ABLCrashHandler 为程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this);}/*** 当 UncaughtException 发生时会转入该函数来处理*/@Overridepublic void uncaughtException(Thread thread, Throwable ex) {if (!handleException(ex) && mDefaultHandler != null) {// 如果用户没有处理则让系统默认的异常处理器来处理mDefaultHandler.uncaughtException(thread, ex);} else {try {Thread.sleep(2000);} catch (InterruptedException e) {Log.e(TAG, "error : ", e);}// 退出程序android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}}/*** 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成* * @param ex* @return true:如果处理了该异常信息;否则返回 false*/private boolean handleException(final Throwable ex) {if (ex == null) {return false;}ex.printStackTrace();//使用 Toast 来显示异常信息new Thread() {@Overridepublic void run() {Looper.prepare();try {ABLApplication.getApplication().getServicesManager().stopScan();ABLApplication.getApplication().getServicesManager().destroy();StringBuffer sb = new StringBuffer();for (Map.Entry<String, String> entry : infos.entrySet()) {String key = entry.getKey();String value = entry.getValue();sb.append(key + "=" + value + "\n");}Writer writer = new StringWriter();PrintWriter printWriter = new PrintWriter(writer);ex.printStackTrace(printWriter);Throwable cause = ex.getCause();while (cause != null) {cause.printStackTrace(printWriter);cause = cause.getCause();}printWriter.close();String message = writer.toString();Log.i(TAG + ".handleException.message", message + " |");String exceptionCode = ABLExceptionCoder.getExceptionCode(message);Log.i(TAG + ".handleException.exceptionCode", exceptionCode + " |");Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出,错误码:" + exceptionCode, Toast.LENGTH_LONG).show();} catch (Exception e) {e.printStackTrace();}Looper.loop();}}.start();// 收集设备参数信息collectDeviceInfo(mContext);// 保存日志文件saveCrashInfo2File(ex);return true;}/*** 收集设备参数信息* @param ctx*/public void collectDeviceInfo(Context ctx) {try {PackageManager pm = ctx.getPackageManager();PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);if (pi != null) {String versionName = pi.versionName == null ? "null" : pi.versionName;String versionCode = pi.versionCode + "";infos.put("versionName", versionName);infos.put("versionCode", versionCode);infos.put("deviceID", Utils.getDeviceID_B());}} catch (NameNotFoundException e) {Log.e(TAG, "an error occured when collect package info", e);} catch (Exception e) {Log.e(TAG, "an error occured when collect package info", e);}Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);infos.put(field.getName(), field.get(null).toString());Log.d(TAG, field.getName() + " : " + field.get(null));} catch (Exception e) {Log.e(TAG, "an error occured when collect crash info", e);}}}/*** 保存错误信息到文件中** @param ex* @return  返回文件名称,便于将文件传送到服务器*/public String saveCrashInfo2File(Throwable ex) {try {StringBuffer sb = new StringBuffer();for (Map.Entry<String, String> entry : infos.entrySet()) {String key = entry.getKey();String value = entry.getValue();sb.append(key + "=" + value + "\n");}Writer writer = new StringWriter();PrintWriter printWriter = new PrintWriter(writer);ex.printStackTrace(printWriter);Throwable cause = ex.getCause();while (cause != null) {cause.printStackTrace(printWriter);cause = cause.getCause();}printWriter.close();String result = writer.toString();String exceptionCode = ABLExceptionCoder.getExceptionCode(result);sb.append("exceptionCode=" + exceptionCode + "\n");sb.append(result);try {String fileName = "crash-" + DateUtil.getNowTime3() + ".log";if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {String path = FileUtils.getSDCardPath() + ABLConfig.SAVE_FOLDER + "/Bug/";File dir = new File(path);if (!dir.exists()) {dir.mkdirs();}File file = new File(path + fileName);if  (!file.exists()){file.createNewFile();}//增加异常信息FileWriter fw = new FileWriter(file, true);fw.write("time:" + DateUtil.getNowTime());fw.write("\r\n");fw.write(sb.toString());fw.write("\r\n\r\n");fw.flush();fw.close();}return fileName;} catch (Exception e) {e.printStackTrace();Log.e(TAG, "an error occured while writing file...", e);}} catch (Exception e) {e.printStackTrace();}return null;}
}

Nativa 层Crash监控

当应用程序发生异常时,Linux内核会生成错误信号并通知当前进程。应用进程接受到错误信号后,可以捕获该信号并执行对应的信号处理函数。而当应用发生验证错误时后发生Crash,Linux有一类专门用于描述Crash的信号。只要在应用程序注册了这些信号的处理函数,当JNI crash时,我们的处理函数就会被调用到,然后获取dump文件再上传,后续的工作就和Java异常逻辑一致了。

ANR剖析

ANR介绍

类型:

  1. KeyDispatchTimeout:对输入事件5秒内无响应
  2. BroadcaseTimeout:在指定时间(默认10秒)无法处理完毕,并且没有结束执行onReceive
  3. ServiceTimeout:指Service在特定时间(默认20秒)内无法处理完成。

ANR分析

如果发送ANR, Logcat会产生对应的日志和一个traces文件,这个文件保存在/data/anr/traces.txt。
可以直接用adb工具获取traces.txt文件:

adb pull /data/anr/traces.txt

可以通过分析log和traces文件信息定位到anr发生的位置和分析原因。

AS提供了一个分析trace文件的工具:Analyze Stacktrace。打开Analyze Stacktrace工具,将traces.txt文件内容复制到窗口,单击Normalize按钮,生成Thread Dump列表。如果某个线程被标红,说明此线程被堵塞了。

提高后台进程存活率

当内存紧张时,优先级低、占用内存大的app进程会优先被杀死。可以提高提高进程优先级使应用在后台的存活时间更长,一般使用以下几种方法实现:

  1. 网络连接

通过长连接心跳和进程保持通信,使进程保持活动状态,但如果系统内存非常紧张,也有可能被杀。

  1. 利用系统现有机制

一般可以注册系统消息(AlarmReceive、BootReceive等),通过系统消息响应挂起进程。

  1. SyncAdapter

利用Android系统提供的账号同步机制实现进程优先级提高。

SyncAdapter是一个系统服务,通过系统的定时器更新应用程序数据ContentProvider,因为Sync服务工作在独立进程,并由系统调度,属于核心进程级别,系统不会杀掉,而使用了SyncAdapter的进程优先级也会提高,优先级变为1,仅低于前台正在运行的进程,因此可以降低应用被系统杀掉概率。

性能优化-稳定性优化相关推荐

  1. 金蝶K/3产品性能稳定性优化指导手册

    金蝶K/3产品性能稳定性优化指导手册 2011-08-15 11:43:05|  分类: ERP应用|字号 订阅  金蝶K/3产品性能稳定性优化指导手册(常见问题)(V3.0) ?金蝶软件(中国)有限 ...

  2. Android性能调优 - 稳定性优化

    1.你们做了哪些稳定性方面的优化? 随着项目的逐渐成熟,用户基数逐渐增多,DAU持续升高,我们遇到了很多稳定性方面的问题,对于我们技术同学遇到了很多的挑战,用户经常使用我们的App卡顿或者是功能不可用 ...

  3. 超级干货:3个性能监控和优化命令详解

    小编为大家整理出了三个有关性能监控和优化命令详细讲解,别看只有三个,但不影响他噎啊,本篇文章很长,涉及top命令.free命令和vmstat命令,真的是很详细的讲解,希望能帮到大家,另外还有两条相关的 ...

  4. bios调整服务器性能模式吗,优化BIOS设置提高显示性能

    优化BIOS设置提高显示性能 互联网   发布时间:2009-04-21 00:57:14   作者:佚名   我要评论 显示性能是集成主板发挥性能最主要的瓶径,尤其是在运行3D游戏等考验显卡性能的程 ...

  5. java如何监控cpu耗时_超级干货:3个性能监控和优化命令讲解

    原标题:超级干货:3个性能监控和优化命令讲解 小编为大家整理出了三个有关性能监控和优化命令详细讲解,别看只有三个,但不影响他噎啊,本篇文章很长,涉及top命令.free命令和 vmstat命令,真的是 ...

  6. 性能分析--视图优化

    性能分析/性能优化-视图优化 优化概述 流畅的操作体验 卡顿 稳定性 内存泄漏,崩溃 省电省流量 代码质量,逻辑 安装包小 安装包过大 UI优化 View层级相同的情况下,尽量使用LinearLayo ...

  7. DB2数据库性能调整和优化(第2版)

    <DB2数据库性能调整和优化(第2版)> 基本信息 作者: 牛新庄 出版社:清华大学出版社 ISBN:9787302325260 上架时间:2013-7-3 出版日期:2013 年7月 开 ...

  8. VMware vSphere 性能优化设计经验+优化方法 | 周末送资料

    VMware vSphere 性能优化设计经验+优化方法 | 周末送资料 https://mp.weixin.qq.com/s?__biz=MjM5NTk0MTM1Mw==&mid=26506 ...

  9. 那些年解的疑难性能问题 --- ext4_fsync优化

    引子 性能问题有时候不像稳定性问题那样,出了bug, ok, fix该bug,搞定它就行了.性能问题如果涉及到文件系统自身架构方面缺陷的话,是很难解的. 不过通过解这些性能问题,使我慢慢地熟悉了文件系 ...

最新文章

  1. (转自Timon's wang blogs)C#实现web信息自动抓取
  2. python 3.10 新增 switch-case 简介
  3. hibernate和spring学习
  4. centos7.2 mysql集群_Centos7.2下安装mysql-group-replication数据库集群
  5. jzoj3852-单词接龙【0/1分数规划,负环】
  6. Java中的值类型:为什么它们不可变?
  7. jsr303jsp页面怎么显示错误信息_springmvc使用JSR-303进行数据校验实例
  8. nil,Nil,NULL,NSNull
  9. 线性判别结合源码分析LDA原理
  10. python判断序列值横穿整个区间的次数
  11. org.apache.commons.math3.linear.FieldMatrix的类关系图
  12. Lack of free swap space on zabbix,增加swap空间
  13. linux eof打印列表,Linux:结合cat和EOF输出到文本文件
  14. .net学科-杨中科-Unity3D视频教程
  15. java中 是什么意思_java中?:是什么意思
  16. 分段线性插值法实验报告_试验二插值法(含实验报告格式)-金锄头文库
  17. Dockerfile MAINTAINER和LABEL指令 语法解析
  18. 关于裁剪CSV文件中的各类数据的代码
  19. python sqlite3事务_Python/SQLite3:无法提交-没有事务是acti
  20. 中国医科大学网络教育学院计算机应用基础,中国医科大学网络教育学院试卷.doc...

热门文章

  1. 基于MATLAB GUI的数字滤波仿真平台设计
  2. Android 消息推送 离线也可以收到通知消息
  3. MySQL - 学习/实践 - 多多翻阅补充
  4. GCPF【异常检测:Density-based】
  5. 妖精的尾巴勇气之旅服务器维护,妖精的尾巴勇气之旅攻略大全 新手攻略开局发展技巧[多图]...
  6. dwcc怎么设置html默认,Dreamweaver CC2018写div自适应页面布局的教程
  7. php读取word模板文件,使用PHPWord对Word文件做模板替换
  8. FTP协议中的登录 上传 下载 新建目录 删除目录 的wireshark包分析(一文看完TCP包分析,附源文件,ppt,操作视频)
  9. 猪队友出卖NV:11系显卡横空出世!
  10. 成绩登记与查询系统App