Android 捕捉app系统中未处理的异常

一:为什么要处理?

其实我们都知道,在开发过程中,自己的app系统或许有许多隐藏的异常,自己没有捕捉到,那么关于异常的捕捉,这是相当重要的,如果系统发生崩溃,那么至少也可以让系统挂在系统之内,不会发现什么系统直接退了,或者是卡死,这样做,能够使得用户体验效果更加,自己也可以发现用户到底出现什么异常,便于自己以后好处理这个问题,优化处理自己的系统。

二:如何解决

在Android 开发中,自身其实带有一个系统默认的异常处理接口,UncaughtExceptionHandler,该接口呢,能够很好的处理程序中发生的异常,所以,往往开发者都喜欢使用它,而且它也是一个非常简单好用的东西。

三:具体实现

(1)实现UncaughtExceptionHandler接口的类

package com.x1.tools;

import java.lang.Thread.UncaughtExceptionHandler;

import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Looper;
import android.util.Log;

import com.x1.dao.SubmitConfigBugs;
import com.x1.ui.R;

/**
 * 未捕获异常捕捉类
 *
 * @author zengtao 2015年5月6日
 *
 *
 */
public class CrashHandlers implements UncaughtExceptionHandler {

	public static final String TGA = "CrashHandlers";

	// 系统默认的UncaughtException处理类
	private Thread.UncaughtExceptionHandler mDefaultHandler;

	// CrashHandler实例
	private static CrashHandlers instance;
	// 程序的Context对象
	private Context mContext;

	private GetPhoneInfo phone;

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

	/** 获取CrashHandler实例 ,单例模式 */
	public synchronized static CrashHandlers getInstance() {
		if (instance == null) {
			instance = new CrashHandlers();
		}
		return instance;
	}

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

	/**
	 * 当UncaughtException发生时会转入该函数来处理
	 */
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!handleException(thread, ex) && mDefaultHandler != null) {
			// 如果用户没有处理则让系统默认的异常处理器来处理
			mDefaultHandler.uncaughtException(thread, ex);
		} else {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				Log.e(TGA, e.toString());
			}
			// 退出程序
			android.os.Process.killProcess(android.os.Process.myPid());
			System.exit(1);
		}
	}

	/**
	 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
	 *
	 * @param ex
	 * @return true:如果处理了该异常信息;否则返回false.
	 */
	private boolean handleException(Thread thread, Throwable ex) {
		if (ex == null) {
			return false;
		}
		// 使用Toast来显示异常信息
		new Thread() {
			@Override
			public void run() {
				Looper.prepare();
				ShowToast.show(mContext, "喵,很抱歉,程序出现异常,即将退出!",
						R.drawable.error_icon);
				Looper.loop();
			}
		}.start();
		// 把异常信息和设备信息上传到服务器
		subMitThreadAndDeviceInfo(mContext, thread, ex);
		return true;
	}

	// 提交信息到服务器
	public void subMitThreadAndDeviceInfo(Context ctx, Thread thread,
			Throwable ex) {
		// 当前用户的账号
		String Account = null;
		if (Config.getCachedAccount(ctx) != null) {
			Account = Config.getCachedAccount(ctx);
		} else {
			Account = "当前无用户登录";
		}
		// 手机设备的信息
		String PhoneModel = phone.PhoneModel;
		String PhoneVersion = phone.PhoneVersion;
		String PhoneResolution = phone.PhoneResolution;
		String ZcmVersion = phone.ZcmVersion;
		String AvailableRom = phone.AvailableRom;
		// 获取当前显示在界面上的Activity的路径加类名
		ActivityManager am = (ActivityManager) ctx
				.getSystemService(Context.ACTIVITY_SERVICE);
		ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
		// 异常信息加异常所在类的绝对路径
		final String content = "thread: " + thread + " , name: "
				+ thread.getName() + ", id: " + thread.getId()
				+ ", exception: " + ex + ", " + cn.getClassName();

		// 执行接口,把异常信息提交到服务器
		new SubmitConfigBugs(0, ctx, Account, PhoneModel, PhoneVersion,
				PhoneResolution, ZcmVersion, AvailableRom, content,
				new SubmitConfigBugs.SuccessCallback() {
					@Override
					public void onSuccess() {
						Log.e(TGA, content + "\n错误信息提交成功");
					}
				}, new SubmitConfigBugs.FailCallback() {

					@Override
					public void onFail() {
						Log.e(TGA, content + "\n错误信息提交失败");
					}
				});
	}

}

(2)实体类:手机信息

package com.x1.tools;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.StatFs;
import android.text.TextUtils;
import android.view.Display;
import android.view.WindowManager;

/**
 * 获取当前手机的设备信息和当前软件的版本
 *
 * @author zengtao 2015年5月6日
 *
 *
 */
public class GetPhoneInfo {
	private Context context;
	public String PhoneModel;
	public String PhoneVersion;
	public String PhoneResolution;
	public String ZcmVersion;
	public String AvailableRom;

	public GetPhoneInfo(Context context) {
		this.context = context;
		PhoneModel = android.os.Build.MODEL;
		PhoneVersion = android.os.Build.VERSION.RELEASE;
		PhoneResolution = getDisplayWAndH();
		ZcmVersion = getAppVersionName(this.context);
		AvailableRom = "ROM剩余存储空间: " + getAvailableInternalMemorySize() + "MB"
				+ ",内置SDCard剩余存储空间: " + getAvailableExternalMemorySize() + "MB";
	}

	// 获取当前版本号
	private String getAppVersionName(Context context) {
		String versionName = "";
		try {
			PackageManager packageManager = context.getPackageManager();
			PackageInfo packageInfo = packageManager.getPackageInfo(
					"com.x1.ui", 0);
			versionName = packageInfo.versionName;
			if (TextUtils.isEmpty(versionName)) {
				return "";
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return versionName;
	}

	// 获取屏幕分辨率
	@SuppressWarnings("deprecation")
	private String getDisplayWAndH() {
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		Display display = wm.getDefaultDisplay();
		String string = "屏幕分辨率: " + display.getWidth() + "x"
				+ display.getHeight();
		return string;
	}

	/**
	 *
	 * @return ROM存储路径
	 */
	private String getInternalMemoryPath() {
		return Environment.getDataDirectory().getPath();
	}

	/**
	 *
	 * @return 内置sd卡路径
	 */
	private String getExternalMemoryPath() {
		return Environment.getExternalStorageDirectory().getPath();
	}

	/**
	 *
	 * @param path
	 *            文件路径
	 * @return 文件路径的StatFs对象
	 * @throws Exception
	 *             路径为空或非法异常抛出
	 */
	private StatFs getStatFs(String path) {
		try {
			return new StatFs(path);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 *
	 * @param stat
	 *            文件StatFs对象
	 * @return 剩余存储空间的MB数
	 *
	 */
	@SuppressWarnings("deprecation")
	private float calculateSizeInMB(StatFs stat) {
		if (stat != null)
			return stat.getAvailableBlocks()
					* (stat.getBlockSize() / (1024f * 1024f));
		return 0.0f;
	}

	/**
	 *
	 * @return ROM剩余存储空间的MB数
	 */
	private float getAvailableInternalMemorySize() {

		String path = getInternalMemoryPath();// 获取数据目录
		StatFs stat = getStatFs(path);
		return calculateSizeInMB(stat);
	}

	/**
	 *
	 * @return 内置SDCard剩余存储空间MB数
	 */
	private float getAvailableExternalMemorySize() {

		String path = getExternalMemoryPath();// 获取数据目录
		StatFs stat = getStatFs(path);
		return calculateSizeInMB(stat);

	}
}

四:总结

以上呢,就可以直接抓捕程序中未处理的异常,而且使得用户在系统自身开发崩溃的时候,不至于造成,直接挂了,所以,在程序中,做这样的处理是非常有必要的,在上面CrashHandlers有一个执行接口的处理,提交数据到服务器,这个是自身开发中所定义的接口,这个可以根据自己的想法实现,我在这个当中,是将手机提交的一些信息,和错误信息发送到服务器,而完成的,这样也可以让自己知道,是什么手机出错,有目的去处理,更好的维护自己的系统。

时间: 2024-08-02 22:09:59

Android 捕捉app系统中未处理的异常的相关文章

C# 截获某个域中未捕获的异常

AppDomain.UnhandledException可以获的异常,却截不下来,求解 AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 在.Net1.0/1.1下,非主线程的未处理异常将被忽略.这本身不是好事,所以2.0后该行为更改了.像你的情况可以要求程序兼容1.1行为.1.添加一个配置文件(App.Config)2.加入runtime节并指定legacyUnhandledExcep

在Android App开发中实现任意Java方法的拦截

在Android的App开发过程中,经常会有一些导致App进程崩溃的Framework层Bug,比如这里讲的Cookie同步引起的崩溃等问题.这种情况下,我们如果能拦截到Framework层的Api调用,对它做个包装把异常捕捉住,就可以避免这个问题了. 当时,纯Java层是做不到的,而Java中的Proxy机制也只能针对自己定义的类,系统Framework层的类就不管用了.这里面说一种通过借鉴Java的JNI机制来实现进程类任意Java方法拦截. 任何一个Java方法在C++层都对应一个Meth

App测试中ios和Android有哪些区别呢?

1 . Android长按home键呼出应用列表和切换应用,然后右滑则终止应用: 2. 多分辨率测试,Android端20多种,ios较少: 3. 手机操作系统,Android较多,ios较少且不能降级,只能单向升级:新的ios系统中的资源库不能完全兼容低版本中的ios系统中的应用,低版本ios系统中的应用调用了新的资源库,会直接导致闪退(Crash): 4.  操作习惯:Android,Back键是否被重写,测试点击Back键后的反馈是否正确:应用数据从内存移动到SD卡后能否正常运行等: 5.

Android中使用UncaughtExceptionHandler来处理未捕获的异常

原文在sparkyuan.me上,转载注明出处:http://sparkyuan.github.io/2016/03/28/使用UncaughtExceptionHandler来处理未捕获的异常/ 所有的App都会发生crash,本文讲解的是如何采集crash信息以供后续开发处理这类问题. 基本思路 当crash发生时,系统会调用UncaughtExceptionHandler的uncaughtException方法,我们可以在这个方法中捕获异常信息,把异常信息存到SD卡中,在合适的时候通过网络

【转载】App测试中ios和Android的区别:

App测试中ios和Android的区别: 1.        Android长按home键呼出应用列表和切换应用,然后右滑则终止应用: 2.        多分辨率测试,Android端20多种,ios较少: 3.        手机操作系统,Android较多,ios较少且不能降级,只能单向升级:新的ios系统中的资源库不能完全兼容低版本中的ios系统中的应用,低版本ios系统中的应用调用了新的资源库,会直接导致闪退(Crash): 4.        操作习惯:Android,Back键是

Android Design 1: Back键和Up键在App导航中的表现

一,概念 1, Back键一直存在android系统中 1-1 任何页面下的返回 1-2 Floating window 1-3 Contexual Action bar/highlight select 1-4 Keyboard 2, Up键是随Android Design出来的. 2-1 android Design 定义的parent container 2-2 app的主界面是不存在Up键的 二,情景分析 1, App内部 1-1 沿逐级深入路径 Back:按照activity在栈中的顺

Android系统中的广播(Broadcast)机制简要介绍和学习计划

在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制:这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用:本文通过一个简单的例子来学习Android系统的广播机制,为后续分析广播机制的源代码作准备. 在Android系统中,为什么需要广播机制呢?广播机制,本质上它就是一种组件间的通信方式,如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式

自己动手清除Android系统中的不良程序

陈跃峰 2014/6/29 最近在使用我的Nexus7时,总是经常莫名其妙的弹出一些广告弹窗,还会自动下载一些应用程序,还会在桌面上生成一个叫做"精彩应用"的图标,这个快捷方式关联的程序显示的内容和广告内容是一致的,很是烦人,使用360手机卫士和乐安全都发现不了这个不良程序,所以就自己动手来清除这个程序. 由于Nexus 7平时用于测试程序,安装的应用很多,手动删除了一些可疑的应用以后还是不行,猜测是某些app可能被人篡改了,就不再去删除app了,而把目光转向了桌面快捷方式上. 既然生

App测试中 ----------------Android和IOS测试区别

1 . Android长按home键呼出应用列表和切换应用,然后右滑则终止应用: 2. 多分辨率测试,Android端20多种,ios较少: 3. 手机操作系统,Android较多,ios较少且不能降级,只能单向升级:新的ios系统中的资源库不能完全兼容低版本中的ios系统中的应用,低版本ios系统中的应用调用了新的资源库,会直接导致闪退(Crash): 4.  操作习惯:Android,Back键是否被重写,测试点击Back键后的反馈是否正确:应用数据从内存移动到SD卡后能否正常运行等: 5.