`
onewayonelife
  • 浏览: 259741 次
  • 性别: Icon_minigender_1
  • 来自: 太原
社区版块
存档分类
最新评论

Android 全局异常处理

 
阅读更多

CrashHandler

package org.wp.activity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Properties;
import java.util.TreeSet;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 * 
 * 
 * UncaughtExceptionHandler:线程未捕获异常控制器是用来处理未捕获异常的。 
 *                           如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
 *                           实现该接口并注册为程序中的默认未捕获异常处理 
 *                           这样当未捕获异常发生时,就可以做些异常处理操作
 *                           例如:收集异常信息,发送错误报告 等。
 * 
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
 */
public class CrashHandler implements UncaughtExceptionHandler {
	/** Debug Log Tag */
	public static final String TAG = "CrashHandler";
	/** 是否开启日志输出, 在Debug状态下开启, 在Release状态下关闭以提升程序性能 */
	public static final boolean DEBUG = true;
	/** CrashHandler实例 */
	private static CrashHandler INSTANCE;
	/** 程序的Context对象 */
	private Context mContext;
	/** 系统默认的UncaughtException处理类 */
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	
	/** 使用Properties来保存设备的信息和错误堆栈信息 */
	private Properties mDeviceCrashInfo = new Properties();
	private static final String VERSION_NAME = "versionName";
	private static final String VERSION_CODE = "versionCode";
	private static final String STACK_TRACE = "STACK_TRACE";
	/** 错误报告文件的扩展名 */
	private static final String CRASH_REPORTER_EXTENSION = ".cr";
	
	/** 保证只有一个CrashHandler实例 */
	private CrashHandler() {
	}

	/** 获取CrashHandler实例 ,单例模式 */
	public static CrashHandler getInstance() {
		if (INSTANCE == null)
			INSTANCE = new CrashHandler();
		return INSTANCE;
	}
	
	/**
	 * 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器
	 * 
	 * @param ctx
	 */
	public void init(Context ctx) {
		mContext = ctx;
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		Thread.setDefaultUncaughtExceptionHandler(this);
	}
	
	/**
	 * 当UncaughtException发生时会转入该函数来处理
	 */
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果用户没有处理则让系统默认的异常处理器来处理
			mDefaultHandler.uncaughtException(thread, ex);
		} else {
			// Sleep一会后结束程序
			// 来让线程停止一会是为了显示Toast信息给用户,然后Kill程序
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				Log.e(TAG, "Error : ", e);
			}
			android.os.Process.killProcess(android.os.Process.myPid());
			System.exit(10);
		}
	}

	/**
	 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
	 * 
	 * @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() {
				// Toast 显示需要出现在一个线程的消息队列中
				Looper.prepare();
				Toast.makeText(mContext, "程序出错啦:" + msg, Toast.LENGTH_LONG).show();
				Looper.loop();
			}
		}.start();
		// 收集设备信息
		collectCrashDeviceInfo(mContext);
		// 保存错误报告文件
		String crashFileName = saveCrashInfoToFile(ex);
		// 发送错误报告到服务器
		sendCrashReportsToServer(mContext);
		return true;
	}

	/**
	 * 收集程序崩溃的设备信息
	 * 
	 * @param ctx
	 */
	public void collectCrashDeviceInfo(Context ctx) {
		try {
			// Class for retrieving various kinds of information related to the
			// application packages that are currently installed on the device.
			// You can find this class through getPackageManager().
			PackageManager pm = ctx.getPackageManager();
			// getPackageInfo(String packageName, int flags)
			// Retrieve overall information about an application package that is installed on the system.
			// public static final int GET_ACTIVITIES
			// Since: API Level 1 PackageInfo flag: return information about activities in the package in activities.
			PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
			if (pi != null) {
				// public String versionName The version name of this package,
				// as specified by the <manifest> tag's versionName attribute.
				mDeviceCrashInfo.put(VERSION_NAME, pi.versionName == null ? "not set" : pi.versionName);
				// public int versionCode The version number of this package, 
				// as specified by the <manifest> tag's versionCode attribute.
				mDeviceCrashInfo.put(VERSION_CODE, pi.versionCode);
			}
		} catch (NameNotFoundException e) {
			Log.e(TAG, "Error while collect package info", e);
		}
		// 使用反射来收集设备信息.在Build类中包含各种设备信息,
		// 例如: 系统版本号,设备生产商 等帮助调试程序的有用信息
		// 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段
		Field[] fields = Build.class.getDeclaredFields();
		for (Field field : fields) {
			try {
				// setAccessible(boolean flag)
				// 将此对象的 accessible 标志设置为指示的布尔值。
				// 通过设置Accessible属性为true,才能对私有变量进行访问,不然会得到一个IllegalAccessException的异常
				field.setAccessible(true);
				mDeviceCrashInfo.put(field.getName(), field.get(null));
				if (DEBUG) {
					Log.d(TAG, field.getName() + " : " + field.get(null));
				}
			} catch (Exception e) {
				Log.e(TAG, "Error while collect crash info", e);
			}
		}
	}
	
	/**
	 * 保存错误信息到文件中
	 * 
	 * @param ex
	 * @return
	 */
	private String saveCrashInfoToFile(Throwable ex) {
		Writer info = new StringWriter();
		PrintWriter printWriter = new PrintWriter(info);
		// printStackTrace(PrintWriter s)
		// 将此 throwable 及其追踪输出到指定的 PrintWriter
		ex.printStackTrace(printWriter);

		// getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。
		Throwable cause = ex.getCause();
		while (cause != null) {
			cause.printStackTrace(printWriter);
			cause = cause.getCause();
		}

		// toString() 以字符串的形式返回该缓冲区的当前值。
		String result = info.toString();
		printWriter.close();
		mDeviceCrashInfo.put(STACK_TRACE, result);

		try {
			long timestamp = System.currentTimeMillis();
			String fileName = "crash-" + timestamp + CRASH_REPORTER_EXTENSION;
			// 保存文件
			FileOutputStream trace = mContext.openFileOutput(fileName, Context.MODE_PRIVATE);
			mDeviceCrashInfo.store(trace, "");
			trace.flush();
			trace.close();
			return fileName;
		} catch (Exception e) {
			Log.e(TAG, "an error occured while writing report file...", e);
		}
		return null;
	}
	
	/**
	 * 把错误报告发送给服务器,包含新产生的和以前没发送的.
	 * 
	 * @param ctx
	 */
	private void sendCrashReportsToServer(Context ctx) {
		String[] crFiles = getCrashReportFiles(ctx);
		if (crFiles != null && crFiles.length > 0) {
			TreeSet<String> sortedFiles = new TreeSet<String>();
			sortedFiles.addAll(Arrays.asList(crFiles));

			for (String fileName : sortedFiles) {
				File cr = new File(ctx.getFilesDir(), fileName);
				postReport(cr);
				cr.delete();// 删除已发送的报告
			}
		}
	}

	/**
	 * 获取错误报告文件名
	 * 
	 * @param ctx
	 * @return
	 */
	private String[] getCrashReportFiles(Context ctx) {
		File filesDir = ctx.getFilesDir();
		// 实现FilenameFilter接口的类实例可用于过滤器文件名
		FilenameFilter filter = new FilenameFilter() {
			// accept(File dir, String name)
			// 测试指定文件是否应该包含在某一文件列表中。
			public boolean accept(File dir, String name) {
				return name.endsWith(CRASH_REPORTER_EXTENSION);
			}
		};
		// list(FilenameFilter filter)
		// 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录
		return filesDir.list(filter);
	}

	private void postReport(File file) {
		// TODO 使用HTTP Post 发送错误报告到服务器
		// 这里不再详述,开发者可以根据OPhoneSDN上的其他网络操作
		// 教程来提交错误报告
	}

	/**
	 * 在程序启动时候, 可以调用该函数来发送以前没有发送的报告
	 */
	public void sendPreviousReportsToServer() {
		sendCrashReportsToServer(mContext);
	}
}

 

CrashApplication

package org.wp.activity;

import android.app.Application;

/**
 * 在开发应用时都会和Activity打交道,而Application使用的就相对较少了。
 * Application是用来管理应用程序的全局状态的,比如载入资源文件。
 * 在应用程序启动的时候Application会首先创建,然后才会根据情况(Intent)启动相应的Activity或者Service。
 * 在本文将在Application中注册未捕获异常处理器。
 */
public class CrashApplication extends Application {
	@Override
	public void onCreate() {
		super.onCreate();
		CrashHandler crashHandler = CrashHandler.getInstance();
		// 注册crashHandler
		crashHandler.init(getApplicationContext());
		// 发送以前没发送的报告(可选)
		crashHandler.sendPreviousReportsToServer();
	}
}

 

在AndroidManifest.xml中注册

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="org.wp.activity" android:versionCode="1" android:versionName="1.0">
	<application android:icon="@drawable/icon" android:label="@string/app_name"
		android:name=".CrashApplication" android:debuggable="true">
		<activity android:name=".MainActivity" android:label="@string/app_name">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
	<uses-sdk android:minSdkVersion="8" />
</manifest>

 

 

 

分享到:
评论
4 楼 kinglo 2013-12-26  
请问生成的文件怎么看?
3 楼 bzlring 2012-12-17  
  很好
2 楼 i5suoi 2012-10-24  
thank you very much
1 楼 lxpanup 2012-06-27  
在什么情况下.去调用这个全局异常处理

相关推荐

    android 全局异常处理

    android 全局异常处理 继承java.lang中的UncaughtExceptionHandler 的构造方法uncaughtException 来捕获异常

    android全局异常处理

    "android全局异常处理"这个主题关注的就是如何在整个Android应用中设置一个统一的错误处理机制,以便在发生异常时进行记录、通知用户或采取其他必要的恢复措施。 首先,我们需要理解Android应用中的异常类型。在...

    Android 捕获全局异常处理

    总结,Android全局异常处理是一个多步骤的过程,包括创建监听器、捕获异常、展示反馈、记录日志、发送邮件以及上传服务器。这些步骤可以帮助开发者快速定位和修复问题,提高应用的稳定性和用户体验。在实际项目中,...

    android全局异常捕获 exception_global

    通过以上步骤,我们可以构建一个健壮的全局异常处理系统,确保Android应用在遇到异常时能够优雅地处理问题,提升用户满意度,同时简化开发者对错误的追踪和修复。这正是“android全局异常捕获 exception_global”这...

    android全局异常捕获

    本文将深入探讨“Android全局异常捕获”的概念、实现方法以及其在实际开发中的应用。 全局异常捕获是指在整个应用程序生命周期内,无论哪个线程或者哪个函数抛出的异常,都能被一个全局的异常处理器捕获到。这样,...

    android工程全局异常处理-将未处理异常日志保存在文件中,可后续开发提交至服务器

    在Android应用开发中,异常处理是一项至关重要的任务,它能够确保程序在遇到错误时不会突然崩溃,而是能够优雅地处理并记录错误信息。本篇主要介绍如何实现一个全局的异常处理机制,以便将未处理的异常日志保存到...

    Android 全局异常捕获

    总的来说,Android全局异常捕获是保证应用健壮性的重要手段。通过对全局异常的捕获和处理,开发者能够更好地追踪和修复问题,提升应用的稳定性和用户体验。同时,这也是遵循“防患于未然”这一编程原则的一个实际...

    Android捕获全局异常并写日志保存到sd卡.rar

    Android捕获全局异常并写日志保存到sd卡,捕获异常消息后输出这个消息,可避免APP强制退出,抛出NullPointException玩玩哈,捕获全局异常是因为有的异常我们捕获不到,捕获时我们采用同步方法,以免单例多线程环境下...

    Android全局异常捕获CrashHandler

    本篇文章将深入探讨Android全局异常捕获`CrashHandler`的相关知识点,以及如何实现和优化这一机制。 1. **什么是全局异常捕获**: 全局异常捕获是指在整个应用程序运行过程中,无论在哪一层级或哪个线程抛出的异常...

    android_异常处理_对异常进行全局捕捉

    在Android开发中,异常处理是一个重要环节,尤其是在产品发布后,为了确保应用的稳定性和良好的用户体验,对异常进行全局捕捉就显得尤为重要。Android提供了UncaughtExceptionHandler接口,用于全局捕捉未捕获的异常...

    Android全局异常捕获及上传服务器

    本主题将深入探讨Android全局异常捕获的机制,以及如何将捕获到的异常信息加密后上传到服务器。 首先,让我们了解如何实现全局异常捕获。在Android中,我们可以创建一个`Thread.UncaughtExceptionHandler`的实现类...

    Android-Android异常捕获以及上传到服务器

    在Android应用开发中,异常处理是一项至关重要的任务,它确保了程序在遇到错误时能够以优雅的方式退出,而不是崩溃,从而提高用户体验。本教程将详细讲解如何在Android中实现异常捕获并将其上传到服务器。 首先,...

    Android全局异常捕获

    因此,实现Android全局异常捕获是一个必要的技术手段,它可以帮助开发者捕获并处理运行时出现的任何异常,避免程序无故终止,并提供调试信息。 全局异常捕获的基本思路是在应用程序的主进程中创建一个顶级的异常...

    Android 全局捕获异常消息示例.rar

    android全局异常捕获,全局捕获异常消息示例,当程序发生Uncaught异常的时候,有该类来接管程序,并记录错误日志,使用系统默认的UncaughtException处理类,实现自定义错误处理,收集错误信息 发送错误报告等操作,还...

    AndroidCrash全局崩溃异常捕获

    1. **全局异常捕获**: 在Android应用中,全局异常捕获通常通过自定义的`UncaughtExceptionHandler`实现。这个处理器会捕获那些没有被应用程序内部捕获的运行时异常,防止应用因为未处理的异常而突然退出。 2. **...

    Android捕获全局异常信息替换系统错误

    为了解决这个问题,开发者通常会选择捕获全局异常信息,以便更优雅地处理错误,同时向用户提供有意义的反馈。本文将深入探讨如何在Android中实现全局异常捕获,以及如何使用系统级对话框显示这些异常信息。 首先,...

    App全局异常捕捉处理工具类

    在Android应用开发中,异常处理是一项至关重要的任务。"App全局异常捕捉处理工具类"就是为了帮助开发者有效地管理和处理应用程序中的各种异常情况,确保程序的稳定性和用户体验。通过使用这样的工具类,开发者可以...

    Android 全局异常捕获实例详解

    总结起来,Android全局异常捕获通过自定义`Thread.UncaughtExceptionHandler`并设置为系统默认异常处理器,可以在应用中全局捕获并处理未被捕获的异常。这不仅可以帮助开发者追踪和修复错误,还能提供更好的用户体验...

Global site tag (gtag.js) - Google Analytics