程序崩溃是应用迭代中不可避免的问题,即使有着5年或者10年经验的程序猿也无法完全保证自己的代码没有任何的bug导致崩溃,现在有一些第三方平台可以帮助我们搜集应用程序的崩溃,比如友盟,详情如下图

虽然能够看到崩溃的日志以及机型等,但还是不是很方便,如果需要精确定位的话需要用户提供崩溃的时间点、机型等信息,所以最好的办法就是我们把崩溃的信息保存在用户的sd卡上,必要的时候发送到后台或者让用户手动提供一下文件,下面就来看如何实现这个功能。

Android 系统提供了处理这类问题的方法,Thread 类中提供了一个方法 setDefaultUncaughtExceptionHandler,设置了这个默认的异常处理器之后当程序发生异常之后就会回调uncaugthException()这个方法,然后可以在这个回调里面捕获异常信息,保存到文件。

话不多说,直接上代码:


package com.hxc.supreme.utils;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast; import com.hxc.supreme.MainApplication; import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map; import static com.hxc.supreme.BuildConfig.DEBUG; /**
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
* 在Application 中调用
* CrashHandlerUtil.getInstance().init(this);
*/
public class XCrashHandlerUtils implements Thread.UncaughtExceptionHandler {
private static final String TAG = "XCrashHandlerUtils";
//系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
//CrashHandler实例
private static XCrashHandlerUtils INSTANCE = new XCrashHandlerUtils();
//程序的Context对象
private Context mContext;
//用来存储设备信息和异常信息
private String crashTip = "似乎遇到了一点小麻烦,程序即将重新启动";
/**
* 文件名
*/
public static final String FILE_NAME = "crash";
/**
* 异常日志 存储位置为根目录下的 Crash文件夹
*/
private static final String PATH = Environment.getExternalStorageDirectory().getPath() +
"/Supreme_crash/";
/**
* 文件名后缀
*/
private static final String FILE_NAME_SUFFIX = ".txt"; public String getCrashTip() {
return crashTip;
} public void setCrashTip(String crashTip) {
this.crashTip = crashTip;
} /**
* 保证只有一个CrashHandler实例
*/
private XCrashHandlerUtils() {
} /**
* 获取CrashHandler实例 ,单例模式
*
* @return 单例
*/
public static XCrashHandlerUtils getInstance() {
return INSTANCE;
} /**
* 初始化
*
* @param context 上下文
*/
public void init(Context context) {
mContext = context;
//获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
//设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
} /**
* 这个是最关键的函数,当系统中有未被捕获的异常,系统将会自动调用 uncaughtException 方法
*
* @param thread 为出现未捕获异常的线程
* @param ex 为未捕获的异常 ,可以通过e 拿到异常信息
*/
@Override
public void uncaughtException(Thread thread, final Throwable ex) {
//导入异常信息到SD卡中
try {
dumpExceptionToSDCard(ex);
} catch (IOException e) {
e.printStackTrace();
}
//这里可以上传异常信息到服务器,便于开发人员分析日志从而解决Bug
// uploadExceptionToServer();
ex.printStackTrace();
//如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就由自己结束自己
if (mDefaultHandler != null) {
mDefaultHandler.uncaughtException(thread, ex);
} else {
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
} } /**
* 将异常信息写入SD卡
*
* @param e
*/
private void dumpExceptionToSDCard(Throwable e) throws IOException {
//如果SD卡不存在或无法使用,则无法将异常信息写入SD卡
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (DEBUG) {
Log.w(TAG, "sdcard unmounted,skip dump exception");
return;
}
}
File dir = new File(PATH);
//如果目录下没有文件夹,就创建文件夹
if (!dir.exists()) {
dir.mkdirs();
}
//得到当前年月日时分秒
long current = System.currentTimeMillis();
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
//在定义的Crash文件夹下创建文件
File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX); try {
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
//写入时间
pw.println(time);
//写入手机信息
dumpPhoneInfo(pw);
pw.println();//换行
e.printStackTrace(pw);
pw.close();//关闭输入流
} catch (Exception e1) {
Log.e(TAG, "dump crash info failed");
} } /**
* 获取手机各项信息
*
* @param pw
*/
private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
//得到包管理器
PackageManager pm = mContext.getPackageManager();
//得到包对象
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
//写入APP版本号
pw.print("App Version: ");
pw.print(pi.versionName);
pw.print("_");
pw.println(pi.versionCode);
//写入 Android 版本号
pw.print("OS Version: ");
pw.print(Build.VERSION.RELEASE);
pw.print("_");
pw.println(Build.VERSION.SDK_INT);
//手机制造商
pw.print("Vendor: ");
pw.println(Build.MANUFACTURER);
//手机型号
pw.print("Model: ");
pw.println(Build.MODEL);
//CPU架构
pw.print("CPU ABI: ");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
pw.println(Build.SUPPORTED_ABIS);
} else {
pw.println(Build.CPU_ABI);
}
} }

然后再Mainapplication的onCreate中调用一下XCrashHandlerUtils.init()方法,接下来写一个bug看一下效果,代码很简单

 package com.hxc.supreme.activity;

 import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView; import com.hxc.supreme.R;
import com.hxc.supreme.service.MainService; /**
* created by huxc on 2017/9/28.
* func: ViewsTestActivity
* email: hxc242313@qq.com
*/ public class ViewsTestActivity extends AppCompatActivity implements View.OnClickListener {
private LinearLayout mainLayout; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_views_test);
// mainLayout = findViewById(R.id.layout_main); View view = LayoutInflater.from(this).inflate(R.layout.button_view, null);
TextView textView = new TextView(this);
textView.setText("add View Dynamic");
textView.setGravity(Gravity.CENTER);
textView.setAllCaps(false);
textView.setTextColor(Color.RED);
mainLayout.addView(textView,new LinearLayout.LayoutParams(300, 200));
} @Override
protected void onResume() {
super.onResume();
} @Override
public void onClick(View view) {
switch (view.getId()) {
}
} }

第36行把findviewById屏蔽了,然后运行了一下程序,看一下logcat中的崩溃信息:

crash文件的保存路径是Supreme_crash,看一下文件中的内容:

和控制台输出的一毛一样,而且还打印出了手机的型号,app的版本等相关信息,大功告成!

Android 应用程序崩溃日志捕捉的更多相关文章

  1. 捕android程序崩溃日志

    主要类别: package com.example.callstatus; import java.io.File; import java.io.FileOutputStream; import j ...

  2. Android平台程序崩溃的类型及原因列举

    Android平台程序崩溃大家都应该遇到过,force close和ANR应该是大家遇到较多的. 这里把Android平台程序崩溃的各种类型做一个简述和原因列举. 1.ANR(可见ANR): 发生场景 ...

  3. iOS - 捕获应用程序崩溃日志

    作为一名iOS移动应用开发者,为了确保你的应用程序正确无误,在将应用程序提交到应用商店之前,你必定会进行大量的测试工作:而且在你测试的过程中应用程序运行的很好,但是在应用商店上线之后,还是有用户抱怨应 ...

  4. iOS 中捕获程序崩溃日志

    iOS 中捕获程序崩溃日志 (2014-04-22 17:35:59) 转载▼     iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下 ...

  5. Android将程序崩溃信息保存本地文件

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...

  6. iOS开发-捕获程序崩溃日志

    iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时 ...

  7. Android在程序崩溃或者捕获异常之后重新启动app

    在Android应用开发中,偶尔会因为测试的不充分导致一些异常没有被捕获,这时应用会出现异常并强制关闭,这样会导致很不好的用户体验,为了解决这个问题,我们需要捕获相关的异常并做处理. 首先捕获程序崩溃 ...

  8. iOS 中捕获程序崩溃日志 (2014-04-22 17:35:59)

    http://blog.sina.com.cn/s/blog_b71d24920101ky2d.html iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软 ...

  9. iOS 捕获程序崩溃日志

    iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者? 下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时的回调动作 NSSetU ...

随机推荐

  1. update-rc.d: error: XXX Default-Start contains no runlevels, aborting.

    root@hm-saas-db:/etc/init.d# update-rc.d confluence disable update-rc.d: error: confluence Default-S ...

  2. tensorflow 1.0 学习:模型的保存与恢复(Saver)

    将训练好的模型参数保存起来,以便以后进行验证或测试,这是我们经常要做的事情.tf里面提供模型保存的是tf.train.Saver()模块. 模型保存,先要创建一个Saver对象:如 saver=tf. ...

  3. 解决关于 ionic3 启动白屏 控制台错误提示:Uncaught SyntaxError Use of const in strict mode.

    今天将项目从ionic2 升级为ionic3 ,ionic serve 运行在网页上无任何错误. 但是将项目打包成为android apk 却一直卡在启动页面 白屏,进不去的情况.后来在android ...

  4. deque源码4(deque元素操作:pop_back、pop_front、clear、erase、insert)

    deque源码1(deque概述.deque中的控制器) deque源码2(deque迭代器.deque的数据结构) deque源码3(deque的构造与内存.ctor.push_back.push_ ...

  5. salesforce lightning零基础学习(十一) Aura框架下APP构造实现

    前面的一些lightning文章讲述了aura的基础知识,aura封装的常用js以及aura下的事件处理.本篇通过官方的一个superbadge来实现一个single APP的实现. superbad ...

  6. java扫盲 接口 Enumeration

    摘要: 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 记起2年前,高中生活最多的是老师的批评.谢谢! -泥沙砖瓦浆木匠 ...

  7. JS中如何理解浮点数?

    本文由云+社区发表 相信大家在平常的 JavaScript 开发中,都有遇到过浮点数运算精度误差的问题,比如 console.log(0.1+0.2===0.3)// false.在 JavaScri ...

  8. 【Flask-RESTPlus系列】Part2:响应编组

    0x00 内容概览 响应编组 基本使用 重命名属性 默认值 自定义字段及多值情况 Url及其他具体字段 复杂结构 列表字段 嵌套字段 api.model()工厂 clone实现复制 api.inher ...

  9. Hyperledger Fabric链码之一

    什么是链码(Chaincode)? 我们知道区块链有3个发展阶段:区块链1.0,区块链2.0,区块链3.0.其中区块链2.0就是各种区块链平台百花齐放的阶段,区块链2.0最大的特点就是智能合约,我们接 ...

  10. [转]MySQL 表锁和行锁机制

    本文转自:http://www.cnblogs.com/itdragon/p/8194622.html MySQL 表锁和行锁机制 行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整 ...