[Android]将应用崩溃信息汇报给开发者并重新启动应用

来源:互联网 发布:马尔可夫 知乎 编辑:程序博客网 时间:2024/05/18 10:24

在开发过程中,虽然经过测试,但在发布后,在广大用户各种各样的运行环境和操作下,可能会发生一些异想不到的错误导致程序崩溃。将这些错误信息收集起来并反馈给开发者,对于开发者改进优化程序是相当重要的。好了,下面就来实现这种功能吧。

(更正:2012年2月9日18时42分07秒)
由于为历史帖原因,以下做法比较浪费,但抓取异常的效果是一样的。
1.对于UI线程(即Android中的主线程)抛出的未捕获异常,将这些异常信息存储起来然后关闭到整个应用程序。并再次启动程序,则进入崩溃信息反馈界面让用户将出错信息以Email的形式发送给开发者。
2.对于非UI线程抛出的异常,则立即唤醒崩溃信息反馈界面提示用户将出错信息发送Email。

效果图如下[CSDN]:

error report
过程了解了,则需要了解的几个知识点如下:
1.拦截UncaughtException
Application.onCreate()是整个Android应用的入口方法。在该方法中执行如下代码即可拦截UncaughtException:

 

2.抓取导致程序崩溃的异常信息
UEHandler是Thread.UncaughtExceptionHandler的实现类,在其public void uncaughtException(Thread thread, Throwable ex)的实现中可以获取崩溃信息,代码如下:

 


3.程序抛异常后,要关闭整个应用
悲催的程序员,唉,以下三种方式都无效了,咋办啊!!!
    3.1android.os.Process.killProcess(android.os.Process.myPid());
    3.2ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        am.restartPackage("lab.sodino.errorreport");
    3.3System.exit(0)
   
    好吧,毛主席告诉我们:自己动手丰衣足食。
    SoftApplication中声明一个变量need2Exit,其值为true标识当前的程序需要完整退出;为false时该干嘛干嘛去。该变量在应用的启动Activity.onCreate()处赋值为false。
    在捕获了崩溃信息后,调用SoftApplication.setNeed2Exit(true)标识程序需要退出,并finish()掉ActErrorReport,这时ActErrorReport退栈,抛错的ActOccurError占据手机屏幕,根据Activity的生命周期其要调用onStart(),则我们在onStart()处读取need2Exit的状态,若为true,则也关闭到当前的Activity,则退出了整个应用了。此方法可以解决一次性退出已开启了多个Activity的Application。详细代码请阅读下面的示例源码。

 

好了,代码如下:

lab.sodino.errorreport.SoftApplication.java

lab.sodino.errorreport.ActOccurError.java

lab.sodino.errorreport.ActErrorReport.java

lab.sodino.errorreport.UEHandler.java

package lab.sodino.uncaughtexception;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.PrintStream;import android.content.Intent;import android.util.Log;/** * @author Sodino E-mail:sodinoopen@hotmail.com * @version Time:2011-6-9 下午11:50:43 */public class UEHandler implements Thread.UncaughtExceptionHandler {private SoftApplication softApp;private File fileErrorLog;public UEHandler(SoftApplication app) {softApp = app;fileErrorLog = new File(SoftApplication.PATH_ERROR_LOG);}@Overridepublic void uncaughtException(Thread thread, Throwable ex) {// fetch Excpetion InfoString info = null;ByteArrayOutputStream baos = null;PrintStream printStream = null;try {baos = new ByteArrayOutputStream();printStream = new PrintStream(baos);ex.printStackTrace(printStream);byte[] data = baos.toByteArray();info = new String(data);data = null;} catch (Exception e) {e.printStackTrace();} finally {try {if (printStream != null) {printStream.close();}if (baos != null) {baos.close();}} catch (Exception e) {e.printStackTrace();}}// printlong threadId = thread.getId();Log.d("ANDROID_LAB", "Thread.getName()=" + thread.getName() + " id=" + threadId + " state=" + thread.getState());Log.d("ANDROID_LAB", "Error[" + info + "]");if (threadId != 1) {// 此处示例跳转到汇报异常界面。Intent intent = new Intent(softApp, ActErrorReport.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.putExtra("error", info);intent.putExtra("by", "uehandler");softApp.startActivity(intent);} else {// 此处示例发生异常后,重新启动应用Intent intent = new Intent(softApp, ActOccurError.class);// 如果没有NEW_TASK标识且是UI线程抛的异常则界面卡死直到ANRintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);softApp.startActivity(intent);// write 2 /data/data/<app_package>/files/error.logwrite2ErrorLog(fileErrorLog, info);// kill App Progressandroid.os.Process.killProcess(android.os.Process.myPid());}}private void write2ErrorLog(File file, String content) {FileOutputStream fos = null;try {if (file.exists()) {// 清空之前的记录file.delete();} else {file.getParentFile().mkdirs();}file.createNewFile();fos = new FileOutputStream(file);fos.write(content.getBytes());} catch (Exception e) {e.printStackTrace();} finally {try {if (fos != null) {fos.close();}} catch (Exception e) {e.printStackTrace();}}}}


 

/res/layout/main.xml

/res/layout/report.xml

 

用到的string.xml资源为:


重要的一点是要在AndroidManifest.xml中对<application>节点设置android:name=".SoftApplication"

 

本文内容归CSDN博客博主Sodino 所有
转载请注明出处: http://blog.csdn.net/sodino/archive/2011/06/13/6540329.aspx