`

Android自定义捕获Application全局异常

 
阅读更多

package qianlong.qlmobile.ui;

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.text.format.Time;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;

public class CrashHandler implements UncaughtExceptionHandler {

	/** Debug Log tag*/ 
	public static final String TAG = "CrashHandler"; 
	/** 是否开启日志输出,在Debug状态下开启, 
	* 在Release状态下关闭以提示程序性能 
	* */ 
	public static final boolean DEBUG = false; 
	/** 系统默认的UncaughtException处理类 */ 
	private Thread.UncaughtExceptionHandler mDefaultHandler; 
	/** CrashHandler实例 */ 
	private static CrashHandler 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 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一会后结束程序 
			try { 
				Thread.sleep(5000); 
			} 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) { 
			Log.w(TAG, "handleException --- ex==null"); 
			return true; 
		} 
		final String msg = ex.getLocalizedMessage(); 
		if(msg == null) {
			return false;
		}
		//使用Toast来显示异常信息 
		new Thread() { 
			@Override 
			public void run() { 
				Looper.prepare(); 
				Toast toast = Toast.makeText(mContext, "程序出错,即将退出:\r\n" + msg,
						Toast.LENGTH_LONG);
				toast.setGravity(Gravity.CENTER, 0, 0);
				toast.show();
//				MsgPrompt.showMsg(mContext, "程序出错啦", msg+"\n点确认退出");
				Looper.loop(); 
			} 
		}.start(); 
		//收集设备信息 
		collectCrashDeviceInfo(mContext); 
		//保存错误报告文件 
		saveCrashInfoToFile(ex); 
		//发送错误报告到服务器 
		//sendCrashReportsToServer(mContext); 
		return true; 
	} 
	
	/** 
	* 在程序启动时候, 可以调用该函数来发送以前没有发送的报告 
	*/ 
	public void sendPreviousReportsToServer() { 
		sendCrashReportsToServer(mContext); 
	} 
	/** 
	* 把错误报告发送给服务器,包含新产生的和以前没发送的. 
	* @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();// 删除已发送的报告 
		} 
		} 
	} 
	private void postReport(File file) { 
		// TODO 发送错误报告到服务器 
	} 
	
	/** 
	* 获取错误报告文件名 
	* @param ctx 
	* @return 
	*/ 
	private String[] getCrashReportFiles(Context ctx) { 
		File filesDir = ctx.getFilesDir(); 
		FilenameFilter filter = new FilenameFilter() { 
			public boolean accept(File dir, String name) { 
				return name.endsWith(CRASH_REPORTER_EXTENSION); 
			} 
		}; 
		return filesDir.list(filter); 
	} 
	
	/** 
	* 保存错误信息到文件中 
	* @param ex 
	* @return 
	*/ 
	private String 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("EXEPTION", ex.getLocalizedMessage());
		mDeviceCrashInfo.put(STACK_TRACE, result); 
		try { 
			//long timestamp = System.currentTimeMillis(); 
			Time t = new Time("GMT+8"); 
			t.setToNow(); // 取得系统时间
			int date = t.year * 10000 + t.month * 100 + t.monthDay;
			int time = t.hour * 10000 + t.minute * 100 + t.second;
			String fileName = "crash-" + date + "-" + time + 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 
	*/ 
	public void collectCrashDeviceInfo(Context ctx) { 
		try { 
			PackageManager pm = ctx.getPackageManager(); 
			PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), 
					PackageManager.GET_ACTIVITIES); 
			if (pi != null) { 
				mDeviceCrashInfo.put(VERSION_NAME, 
						pi.versionName == null ? "not set" : pi.versionName); 
				mDeviceCrashInfo.put(VERSION_CODE, ""+pi.versionCode); 
			} 
		} catch (NameNotFoundException e) { 
			Log.e(TAG, "Error while collect package info", e); 
		} 
		//使用反射来收集设备信息.在Build类中包含各种设备信息, 
		//例如: 系统版本号,设备生产商 等帮助调试程序的有用信息 
		//具体信息请参考后面的截图 
		Field[] fields = Build.class.getDeclaredFields(); 
		for (Field field : fields) { 
			try { 
				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); 
			} 
		} 
	} 

}

 

 

用法:
//全局数据存储
public class App extends Application {
private final static float HEAP_UTILIZATION = 0.75f;
private final static int MIN_HEAP_SIZE = 6* 1024* 1024 ;
   @Override
   public void onCreate() {
	super.onCreate();
		
	// 异常处理,不需要处理时注释掉这两句即可!
	CrashHandler crashHandler = CrashHandler.getInstance(); 
	// 注册crashHandler 
	crashHandler.init(getApplicationContext()); 
		
	// 优化内存,以下非必须!
	VMRuntime.getRuntime().setTargetHeapUtilization(HEAP_UTILIZATION);
	VMRuntime.getRuntime().setMinimumHeapSize(MIN_HEAP_SIZE); 
        //changeMetrics(this);//修改屏幕Density
        ......
   }
}
 异常时写入文件,下面是data/data中生成的文件: 

 

 

//
private static final boolean DebugFlag = false;
//修改屏幕Density
public static void changeMetrics(Context context) {
        DisplayMetrics curMetrics = context.getResources().getDisplayMetrics();
        if(!DebugFlag) {
	        if (curMetrics.densityDpi == DisplayMetrics.DENSITY_HIGH) {
	        	DisplayMetrics metrics = new DisplayMetrics();
	        	metrics.scaledDensity = 1.0f;
	        	metrics.density = 1.0f;
	        	metrics.densityDpi = DisplayMetrics.DENSITY_MEDIUM;
	        	metrics.xdpi = DisplayMetrics.DENSITY_MEDIUM;
	        	metrics.ydpi = DisplayMetrics.DENSITY_MEDIUM;
	        	metrics.heightPixels = curMetrics.heightPixels;
	        	metrics.widthPixels = curMetrics.widthPixels;
	        	context.getResources().getDisplayMetrics().setTo(metrics);
	        }
        } else {
        	DisplayMetrics metrics = new DisplayMetrics();
        	metrics.scaledDensity = (float)(130/160.0);
        	metrics.density = (float)(130/160.0);
        	metrics.densityDpi = 130;
        	metrics.xdpi = 130;
        	metrics.ydpi = 130;
        	metrics.heightPixels = curMetrics.heightPixels;
        	metrics.widthPixels = curMetrics.widthPixels;
        	context.getResources().getDisplayMetrics().setTo(metrics);
        }
}

 

参考: 
http://bigcat.easymorse.com/?p=1152 

 

 

分享到:
评论

相关推荐

    Android 全局异常捕获

    在Android中,我们通常通过创建一个自定义的`Application`类来实现全局异常捕获。自定义`Application`类需要继承自Android的`Application`基类,并在`AndroidManifest.xml`中指定我们的自定义类。在自定义的`...

    android全局异常捕获 exception_global

    在Android开发中,全局异常捕获是一个至关重要的实践,它能确保应用在遇到未预期的错误时仍能保持稳定,并提供良好的用户体验。标题"android全局异常捕获 exception_global"和描述都指向了这个主题,表明我们将探讨...

    android全局异常捕获

    在Android中,我们可以通过创建一个自定义的`Thread.UncaughtExceptionHandler`并将其设置为应用的默认异常处理器来实现全局异常捕获。`Thread.UncaughtExceptionHandler`是Java线程处理未捕获异常的接口,当线程抛...

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

    本文将深入探讨如何在Android中实现全局异常捕获,以及如何使用系统级对话框显示这些异常信息。 首先,我们需要创建一个自定义的应用程序(Application)类,它是整个应用程序的入口点,可以在此监听并处理全局异常。...

    android捕获全局异常,CrashHandler完整工程源码

    本篇将详细介绍如何在Android中实现全局异常捕获,并提供一个名为"CrashHandler"的完整工程源码。 首先,我们需要了解Android中的异常处理机制。在Java中,每个线程都有一个默认的`UncaughtExceptionHandler`,当...

    捕获全局异常.rar

    本教程将深入探讨如何在Android中实现全局异常捕获。 首先,我们来看`CrashHandler.java`这个文件。这是一个自定义的全局异常处理器类,它的主要职责是注册到系统,当应用程序发生未捕获的异常时,该处理器会被调用...

    Android全局异常捕获

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

    android开发,异常处理,捕获异常上传到bugly,捕获异常库module

    在Android中,我们可以使用全局的UncaughtExceptionHandler来捕获未被捕获的异常,这是系统默认的异常处理器。通过重写其handleException方法,我们可以自定义处理流程,例如记录日志或弹出用户友好的错误信息。 接...

    Android 全局异常捕获实例详解

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

    安卓日志分析崩溃拦截相关-Android捕获全局异常源码.rar

    这个压缩包“Android捕获全局异常源码”可能包含了一些用于实现这一目的的代码示例和工具。 首先,我们要理解什么是全局异常捕获。在Android系统中,如果应用程序抛出了未被捕获的异常,系统会默认终止该应用,这对...

    Android全局捕获崩溃异常记录日志保存至本地并定时删除

    为了解决这个问题,开发者通常会实现全局异常捕获机制,以便在程序崩溃时记录异常信息,并将这些日志保存到本地。此外,为了维护存储空间的有效利用,日志还需要定时清理。本文将详细介绍如何在Android中实现这一...

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

    通过在Application的onCreate方法中进行这样的设置,应用的生命周期在启动时就会注册这个自定义的全局异常处理器。 此外,在测试中,可以通过创建一个新的线程,并在其中抛出一个NullPointerException异常来模拟子...

    Android异常捕获

    Android异常捕获机制主要涉及到Java语言的异常处理和自定义的CrashHandler。本文将深入探讨这两个方面,并结合`YangdechengApplication`这个应用实例,来阐述如何在Android应用中有效地进行异常管理。 一、Java异常...

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

    在Android中,为了全局捕获异常,我们通常会在应用程序的主Activity或Application类中设置一个UncaughtExceptionHandler。这是Java的默认异常处理器,当所有其他catch块都未能捕获异常时,它会被调用。以下是如何...

    Android 全局异常处理

    总结,Android全局异常处理是通过创建自定义的`Thread.UncaughtExceptionHandler`并设置为全局处理器来实现的,这样可以统一处理所有未捕获异常,保证应用的稳定性。同时,通过存储异常信息到本地和上传到服务器,...

    Android异常捕获demo(可以捕获ANR+UncaughtExceptionHandler)

    - **全局异常处理**:设置`UncaughtExceptionHandler`来捕获未被其他catch块处理的异常。 - **避免ANR**:确保主线程不执行耗时操作,使用AsyncTask或其他异步方法处理后台任务。 - **日志记录**:使用Android的...

    android非捕获异常处理

    为避免这种情况,开发者可以自定义全局的非捕获异常处理器,以更优雅的方式处理这些异常,同时收集有用的调试信息。 下面是一种实现全局非捕获异常处理的常见方法: 1. 创建一个实现了`Thread....

    安卓全局异常捕获处理

    "安卓全局异常捕获处理"是Android应用开发中的一个核心概念,旨在统一处理应用程序中可能出现的各种异常,防止程序崩溃,并提供友好的错误提示或日志记录,以便于开发者进行调试和优化。 首先,我们来看一下如何...

Global site tag (gtag.js) - Google Analytics