android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地

教你如何抓取应用崩溃日志,保存到本地,或者增加一些友好提示,如果有需要还可以上传到服务器。

大家好,又见面了,我是全栈君。

应用发生crash之后要查看log,判断问题出在什么地方,可是一旦应用发布出去,就要想办法把用户的崩溃日志拿到分析。

所以要在发生crash之后抓取log,然后上传到服务器,方便开发者查看,现在都有很多第三方做这方面的服务,这里说下如何自己来实现。

其实原理很简单,应用出现异常后,会由默认的异常处理器来处理异常,

我们要做的就是把这个任务接管过来,自己处理异常,包括收集日志,保存到本地,然后上传到服务器。

下面是自己实现的异常处理类。

public class CrashHandler implements UncaughtExceptionHandler {
	public static final String TAG = "CrashHandler";

	// 系统默认的UncaughtException处理类
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	// CrashHandler实例
	private static CrashHandler INSTANCE = new CrashHandler();
	// 程序的Context对象
	private Context mContext;
	// 用来存储设备信息和异常信息
	private Map<String, String> infos = new HashMap<String, String>();

	// 用于格式化日期,作为日志文件名的一部分
	private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
	private String nameString;

	/** 保证只有一个CrashHandler实例 */
	private CrashHandler() {
	}

	/** 获取CrashHandler实例 ,单例模式 */
	public static CrashHandler getInstance() {
		return INSTANCE;
	}

	/**
	 * 初始化
	 * 
	 * @param context
	 */
	public void init(Context context) {
		mContext = context;
		// 获取系统默认的UncaughtException处理器
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		// 设置该CrashHandler为程序的默认处理器
		Thread.setDefaultUncaughtExceptionHandler(this);
		nameString = BmobUserManager.getInstance(mContext).getCurrentUserName();
	}

	/**
	 * 当UncaughtException发生时会转入该函数来处理
	 */
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果用户没有处理则让系统默认的异常处理器来处理
			mDefaultHandler.uncaughtException(thread, ex);
		} else {
			try {
				Thread.sleep(3000);
			} 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(Throwable ex) {
		if (ex == null) {
			return false;
		}
		WonderMapApplication.getInstance().getSpUtil().setCrashLog(true);// 每次进入应用检查,是否有log,有则上传
		// 使用Toast来显示异常信息
		new Thread() {
			@Override
			public void run() {
				Looper.prepare();
				Toast.makeText(mContext, "很抱歉,程序出现异常,正在收集日志,即将退出", Toast.LENGTH_LONG)
						.show();
				Looper.loop();
			}
		}.start();
		// 收集设备参数信息
		collectDeviceInfo(mContext);
		// 保存日志文件
		String fileName = 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);
			}
		} catch (NameNotFoundException 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 返回文件名称,便于将文件传送到服务器
	 */
	private String saveCrashInfo2File(Throwable ex) {

		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();
		L.d(WModel.CrashUpload, result);
		sb.append(result);
		try {
			long timestamp = System.currentTimeMillis();
			String time = formatter.format(new Date());
			String fileName = nameString + "-" + time + "-" + timestamp
					+ ".log";
			if (Environment.getExternalStorageState().equals(
					Environment.MEDIA_MOUNTED)) {
				String path = WMapConstants.CrashLogDir;
				File dir = new File(path);
				if (!dir.exists()) {
					dir.mkdirs();
				}
				FileOutputStream fos = new FileOutputStream(path + fileName);
				fos.write(sb.toString().getBytes());
				fos.close();
			}
			return fileName;
		} catch (Exception e) {
			Log.e(TAG, "an error occured while writing file...", e);
		}
		return null;
	}

}

使用方式如下:

在Application的onCreate中加上下面

CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(this);

这样一来,应用发生crash,自动保存log到本地了。

其实这样还有一个好处,就是不用在logcat里面翻来翻去找日志,直接到本地文件夹打开看就是了。

作者:jason0539

博客:http://blog.csdn.net/jason0539(转载请说明出处)

扫码关注我微信公众号

android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/121313.html原文链接:https://javaforall.net

(0)
上一篇 2022年3月11日 下午4:00
下一篇 2022年3月11日 下午4:00


相关推荐

  • 2025全面攻略:Midjourney API使用教程 – 从入门到精通【最新实用指南】

    2025全面攻略:Midjourney API使用教程 – 从入门到精通【最新实用指南】

    2026年3月16日
    2
  • 迭代阈值法

    迭代阈值法通过迭代方法选择阈值 计算方法如下 1 选择灰度图的平均值作为初始阈值 T0 2 计算小于等于 T0 的平均值 T1 和大于 T0 的平均值 T2 3 新的阈值为 T T1 T2 2 4 比较 T 和 T0 若相等 则返回 T 即为迭代阈值 否则 T0 T 重复 1 3

    2026年3月26日
    2
  • 渗透测试的8个步骤—转载

    渗透测试的8个步骤—转载渗透测试的 8 个步骤展现一次完整的渗透测试过程及思路 发布时间 2017 年 10 月 25 日 15 11 nbsp nbsp nbsp 浏览量 1104 nbsp 渗透测试这个事情不是随便拿个工具就可以做了 要了解业务还需要给出解决方案 nbsp 之前安全加介绍了金融行业 nbsp 实战微信银行渗透测试 nbsp 运营商 nbsp 渗透测试实战 nbsp 今天让我们来说说 nbsp 渗透测试 nbsp 的流程及渗透测试相关概念 渗透测试流程渗透测试与入侵的最大区别渗透测

    2026年3月19日
    2
  • JRTPLIB_刘伯温传简介

    JRTPLIB_刘伯温传简介jrtplib是一个基于C++、面向对象的RTP封装库,最新的版本是3.9.1(2011年11月)。为了与RFC3550相兼容,3.x.x版本经过完全重写,现在它提供了一些非常有用的组件,这些组件为构建各种各样的RTP应用程序开发提供了有用的帮助。较旧的2.x版本依然可用,但是不兼容RFC3550。1.特性    jrtplib支持定义于RFC3550中的RTP协议,它使得发送和接

    2022年7月28日
    5
  • oracle模糊匹配优化,Oracle 模糊查询 优化

    oracle模糊匹配优化,Oracle 模糊查询 优化1 字段 like 关键字 字段包含 关键字 的记录即使在目标字段建立索引也不会走索引 速度最慢 2 字段 like 关键字 字段以 关键字 开始的记录可以使用到在目标字段建立的升序索引 3 字段 like 关键字 字段以 关键字 结束的记录可以使用到目标字段建立的降序索引对于无法使用索引的 关键字 模式 有没有办法优化

    2026年3月16日
    1
  • goland2021.8激活码(注册激活)[通俗易懂]

    (goland2021.8激活码)本文适用于JetBrains家族所有ide,包括IntelliJidea,phpstorm,webstorm,pycharm,datagrip等。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月25日
    264

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号