安卓应用崩溃信息捕获,并立即重新启动应用---------------android软件开发第四步

5 篇文章 0 订阅
2 篇文章 0 订阅

       在最近的工作中,遇到一些事情,让我倍受打压,但是在这种不断的打压情况下也猛然促发了我的精气神,不知怎的就头脑一冒烟想出一些奇怪的想法,并将它实现了出来,下面我们来慢慢聊聊这个:某码农在艰辛的工作了一月之后,经过奋战终于将产品的稳定性做了一个很大的提高,但是呢,上级领导总是对码农的技术没有做出任何的肯定,并闲来没事就狂点应用进行无脑暴力测试,直到点出问题为止;于是某日经过无脑测试之后找到码农说你这是干什么的,怎么到现在还存在【应用崩溃】的现象,当时码农那个心凉啊,一想难道前段时间的工作都白费了?于是让领导来拍砖:结果看着看着就傻眼了.........于是,码农就想我一定要干掉你这个毛病,让你没话可说,于是就有了下面的代码和功能实现:采集应用崩溃信息存储在本地,并在应用崩溃后1秒钟的时间内重新启动应用,让boss去吃屎吧!

(一)我们在应用启动的时候都会开启一个Application,所以我们自己需要一个全局的Application去监控整个应用的异常情况,下面为代码实现:

import android.app.Application;

public class AnnouncementApplication extends Application {

	/**
	 * 
	 * @author cuiyongzhi
	 */

	private CrashHandler mCrashHandler;

	@Override
	public void onCreate() {

		super.onCreate();

		mCrashHandler = CrashHandler.getInstance();

		mCrashHandler.init(this);

		Thread.setDefaultUncaughtExceptionHandler(mCrashHandler);

	}

}

需要在我们的xml文件中添加该Application的声明注册;


(二)下面我们就将继承UncaughtExceptionHandler去对应用做全体的监控,该类代码实现如下:

/**
 * @author cuiyongzhi 在Application中统一捕获异常,保存到文件中下次再打开时上传
 */

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;
	AnnouncementApplication application;
	// 用来存储设备信息和异常信息
	private Map<String, String> infos = new HashMap<String, String>();

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

	/** 保证只有一个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);
	}

	/**
	 * 当UncaughtException发生时会转入该函数来处理
	 */
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果用户没有处理则让系统默认的异常处理器来处理
			mDefaultHandler.uncaughtException(thread, ex);
		} else {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				Log.e(TAG, "error : ", e);
			}

			Intent intent = new Intent(mContext.getApplicationContext(),
					HurryMainActivity.class);
			PendingIntent restartIntent = PendingIntent.getActivity(
					mContext.getApplicationContext(), 0, intent,
					Intent.FLAG_ACTIVITY_NEW_TASK);
			// 退出程序
			AlarmManager mgr = (AlarmManager) mContext
					.getSystemService(Context.ALARM_SERVICE);
			mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500,
					restartIntent); // 1秒钟后重启应用
			
			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;
		}
		// 使用Toast来显示异常信息
		new Thread() {
			@Override
			public void run() {
				Looper.prepare();
				Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG)
						.show();
				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);
	// }
	// } 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) {

		Log.i(TAG,
				"saveCrashInfo2File=============================================");
		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");
		}

		String sdk = android.os.Build.VERSION.SDK; // SDK号
		String model = android.os.Build.MODEL; // 手机型号

		String release = android.os.Build.VERSION.RELEASE; // android系统版本号

		sb.append("sdk=" + sdk + "\n");
		sb.append("model=" + model + "\n");
		sb.append("release=" + release + "\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();
		sb.append(result);
		try {
			// 判断SD卡是否存在,并且是否具有读写权限
			if (Environment.getExternalStorageState().equals(
					Environment.MEDIA_MOUNTED)) {
				// 获得存储卡的路径
				String sdpath = Environment.getExternalStorageDirectory() + "/";
				String mSavePath = sdpath + "HurryTime";

				File file = new File(mSavePath);
				// 判断文件目录是否存在
				if (!file.exists()) {
					file.mkdir();
				}
				File apkFile = new File(mSavePath, "catch.xml");
				FileOutputStream fos = new FileOutputStream(apkFile);
				fos.write(sb.toString().getBytes());

				fos.close();

			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
}
下面对其中部分做详细说明:

SDK、model、release分别为获取到的用户的手机信息:SDK号、手机型号、android版本号;

上图中的①为获取到应用崩溃的错误信息sb,②为在应用中将错误信息和用户手机相关信息报错到本地SD卡中的文件中;

在上图中的标识处即为发生崩溃后的处理信息,其中就是为在应用关闭后的500ms内重新开启应用;



压力是我们前行的动力,但是我们不想在这种情况下前行----------------------------------心里一万句CNM飘过,欢迎访问个人博客(http://cuiyongzhi.com)!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值