android全局异常检测并处理

作为一名android开发人员,开发好的应用推广出去的话,很难预料到会有哪些异常。

而那些未被捕获的异常如果经常出现的话,会很影响用户体验效果的。

android其实对于这些未知的异常,有一个捕获机制的。

好了,废话不多上,上一个例子。

首先是一个异常处理类:

public class ExceptionHandler implements UncaughtExceptionHandler {
	/** Debug Log tag */
	public static final String TAG = "CrashHandler";
	/**
	 * 是否开启日志输出,在Debug状态下开启, 在Release状态下关闭以提示程序性能
	 * */
	public static final boolean DEBUG = true;
	/** 系统默认的UncaughtException处理类 */
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	/** CrashHandler实例 */
	private static ExceptionHandler instance;
	/** 程序的Context对象 */
	private Context mContext;

	/** 使用Properties来保存设备的信息和错误堆栈信息 */
	private Properties mDeviceCrashInfo = new Properties();
	private static final String VERSION_NAME = "versionName";
	private static final String VERSION_CODE = "versionCode";
	private static final String OS_VER= "osVer";//安卓版本
	private static final String Device_TYPE = "deviceType";//手机型号
	
	private static final String STACK_TRACE = "STACK_TRACE";
	/** 错误报告文件的扩展名 */
	private static final String CRASH_REPORTER_EXTENSION = ".cr";

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

	/** 获取CrashHandler实例 ,单例模式 */
	public static ExceptionHandler getInstance() {
		if (instance == null) {

			instance = new ExceptionHandler();
		}
		return instance;
	}

	/**
	 * 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器
	 * @param ctx
	 */
	public void init(Context ctx) {
		mContext = ctx;
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		Thread.setDefaultUncaughtExceptionHandler(this);
	}

	/**
	 * 当UncaughtException发生时会转入该函数来处理
	 */
	boolean flag=true;
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if(flag==false)return;
		flag=false;
		
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果用户没有处理则让系统默认的异常处理器来处理
			Log.e("TEST", "handle Throwable");
		} else {
			// Sleep一会后结束程序
			Log.e("TEST", "sleep and finish");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				Log.e(TAG, "Error : ", e);
			}
			Log.e("TEST", "异常");
		}
//		mDefaultHandler.uncaughtException(thread, ex);
		SPZApplication.getInstance().exit();
	}

	/**
	 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
	 * @param ex
	 * @return true:如果处理了该异常信息;否则返回false
	 */
	private boolean handleException(Throwable ex) {
		if (ex == null) {
			return true;
		}
		final String msg = ex.getLocalizedMessage();
		// 使用Toast来显示异常信息
		new Thread() {
			@Override
			public void run() {
				Looper.prepare();
				Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出。\n"+msg, Toast.LENGTH_LONG)
						.show();
				Looper.loop();
			}

		}.start();
		// 收集设备信息
		collectCrashDeviceInfo(mContext);
		// 保存错误报告文件
		saveCrashInfoToFile(ex);
		// 发送错误报告到服务器
		
		return true;
	}

	/**
	 * 保存错误信息到文件中
	 * 
	 * @param ex
	 * @return
	 */
	private void saveCrashInfoToFile(Throwable ex) {
	
		Writer info = new StringWriter();
		PrintWriter printWriter = new PrintWriter(info);
		ex.printStackTrace(printWriter);

		Throwable cause = ex.getCause();
		while (cause != null) {
			cause.printStackTrace(printWriter);
			cause = cause.getCause();
		}

		String result = info.toString();
		printWriter.close();
		mDeviceCrashInfo.put(STACK_TRACE, result);

		try {
			long timestamp = System.currentTimeMillis();
			String fileName = "crash-" + timestamp + CRASH_REPORTER_EXTENSION;
			File file = new File(Constant.StorageLocation_CRASH+File.separator+fileName);
			if(!file.getParentFile().exists()){
				file.getParentFile().mkdirs();
			}
			FileOutputStream trace = new FileOutputStream(file);
			mDeviceCrashInfo.store(trace, "");
			trace.flush();
			trace.close();
		} catch (Exception e) {
			Log.e(TAG, "an error occured while writing report file...", e);
		}
	}

	/**
	 * 收集程序崩溃的设备信息
	 * @param ctx
	 */
	public void collectCrashDeviceInfo(Context ctx) {
		PackageInfo versionInfo = Utils.getVersionInfo(ctx);
		mDeviceCrashInfo.put(VERSION_NAME, new String(versionInfo.versionName).trim());
		mDeviceCrashInfo.put(VERSION_CODE, new String(Integer.toString(versionInfo.versionCode)).trim());
		mDeviceCrashInfo.put(OS_VER, new String(Build.VERSION.RELEASE).trim());
		mDeviceCrashInfo.put(Device_TYPE, new String(Build.MODEL).trim());
	}
}

如果想全局的接收异常,每个应用对应唯一的Application,我们就应该注册这个application的UncaughtExceptionHandler。

SPZApplication 

Application里面有一个List<Activity>,是存储用来结束所有的Activity的,也用过System.exit(1);等等方式,都不理想,这些方法只能结束一个Activity

public class SPZApplication extends Application {

	private List<Activity> activityList = new LinkedList<Activity>();

	private static SPZApplication instance;

	// 单例模式中获取唯一的MyApplication实例
	public static SPZApplication getInstance() {
		return instance;
	}

	// 添加Activity到容器中
	public void addActivity(Activity activity) {
		activityList.add(activity);
	}

	// 遍历所有Activity并finish
	public void exit() {
		for (Activity activity : activityList) {
			if(!activity.isFinishing()){
				activity.finish();
			}
		}
	}

	@Override
	public void onCreate() {
		super.onCreate();
		instance=this;
		ExceptionHandler exceptionHandler = ExceptionHandler.getInstance();
		// 注册crashHandler
		exceptionHandler.init(getApplicationContext());
		// 发送以前没发送的报告(可选)
		// exceptionHandler.sendPreviousReportsToServer();
	}
}

manifest.xml中的注册:

<application
        android:allowBackup="true"
        android:icon="@drawable/showicon"
        android:label="@string/app_name"
        android:name="com.lock.activity.SPZApplication"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.bpo.lock.activity.CheckActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
</application>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失落夏天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值