Android学习之 应用崩溃异常处理

           针对应用程序不可避免的出现一些异常使得应用崩溃的情况,当然android系统本身在软件遇到没有捕获的异常之后  系统会弹出一个默认的强制关闭对话框,

但是只实际的应用开发中  我们通常会有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,在UI上给出友好的提示 或是上传到服务器供开发者分析出现异常的具体原因。

实现机制:在Application中注册我们自己的线程未捕获异常处理器<实现UncaughtExceptionHandler接口>

先了解这两个类:android.app.Application和java.lang.Thread.UncaughtExceptionHandler

Application:用来管理应用程序的全局状态

Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些个性化的异常处理操作。


下面是具体的代码实现:

1、线程未捕获异常处理器,用来处理未捕获的异常类:CrashHandler.java

package com.ice.android.common.excepion;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

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

import com.ice.android.common.utils.SdcardUtil;
/**
 * 线程未捕获异常处理器,用来处理未捕获的异常
 * 当程序发生Uncaught异常的时候,由该类来处理程序并记录/发送错误报告
 * @author ice
 * 
 * 参考博客:http://blog.csdn.net/liuhe688/article/details/6584143
 */
public class CrashHandler implements UncaughtExceptionHandler {

	public static final String TAG = "CrashHandler";
	
	/** 系统默认的 UncaughtException 处理对象   */
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	
	private Context mContext;

	/**
	 * 用于存储手机设备的参数信息
	 */
	private Map<String, String> infos = new HashMap<String, String>(); 
	
	private static CrashHandler mCrashHandler;
	
	private CrashHandler(){
		
	}
	
	/** 获取CrashHandler实例 ,单例模式   */
	public synchronized static CrashHandler getInstance(){
		if(mCrashHandler == null){ 
			mCrashHandler = new CrashHandler();
		}
		return mCrashHandler;
	}
	
	public void init(Context context){
		mContext = context;
		/** 获得系统默认的UncaughtException处理对象  */
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		/** 设置mCrashHandler为该程序默认的 UncaughtException处理对象 */
		Thread.setDefaultUncaughtExceptionHandler(this);
	}

	/**
	 * 当UncaughtException发生时,会调用该方法 
	 */
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		Log.d(TAG, "应用发生未知错误");
		if(!handleException(ex) && mDefaultHandler != null){
			// 如果用户自定义的mCrashHandler没有处理,则让系统默认的异常处理器来进行处理
			mDefaultHandler.uncaughtException(thread, ex);
		}else{
			// 3秒后退出程序
			try {  
                Thread.sleep(3000);  
            } catch (InterruptedException e) {  
                Log.e(TAG, "error : ", e);  
            }
			Process.killProcess(Process.myPid());
			System.exit(1);
		}
	}
	
	
	/**
	 * 自定义错误处理、收集错误信息、发送错误报告
	 * @param ex
	 * @return
	 */
	private boolean handleException(Throwable ex){
		if(ex ==  null){
			return false;
		}
		// 使用Toast来显示异常信息
		new Thread(){
			@Override
			public void run() {
				/**
				 * 这里我们可以使用Toast简单的来显示异常后信息,也可以弹出对话框来显示  具体看项目的不同需求啦
				 */
				Looper.prepare();  
				Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出", Toast.LENGTH_LONG).show();
				Looper.loop();    
			}
		}.start();		
		// 收集设备参数信息
		collectDeviceInfo(mContext);
		// 保存日志文件
		saveCrashInfo2File(ex,mContext);
		return true;
	}

	
	/**
	 * 收集手机设备参数信息
	 * @param ctx
	 */
	private void collectDeviceInfo(Context ctx) {
		try {
			PackageManager pm = ctx.getPackageManager();
			PackageInfo packageInfo = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
			if(packageInfo != null){
				String versionName = packageInfo.versionName == null ? "null": packageInfo.versionName;
				String versionCode = packageInfo.versionCode + "";
				Log.d(TAG, "versionName:"+versionName+" ,versionCode:"+versionCode);
				infos.put("versionName", versionName);
				infos.put("versionCode", versionCode);
			}
		} catch (NameNotFoundException e) {
			Log.e(TAG, "an error occured when collect package info", e); 
		}
	}
	
	
	/**
	 * 保存错误信息到本地sdCard上指定目录的日志文件
	 * @param ex
	 * @param ctx
	 * @return 返回文件名 便于将文件上传至服务器
	 */
	private String saveCrashInfo2File(Throwable ex,Context ctx) {
		StringBuilder sb = new StringBuilder();
		for(String keySet:infos.keySet()){
			sb.append(keySet+"="+infos.get(keySet)+"\n");
		}
		Writer writer = new StringWriter();
		PrintWriter printWriter = new PrintWriter(writer);
		ex.printStackTrace(printWriter);
		
		Throwable cause = ex.getCause();
		while(cause != null){
			cause.printStackTrace(printWriter);
			cause = cause.getCause();
		}
		printWriter.close();
		
		String result = writer.toString();
		sb.append(result);
		Log.d(TAG, sb.toString());
		
		long timestamp = System.currentTimeMillis();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		String time = sdf.format(new Date(timestamp));
		String fileName = "crash_"+time+".log";
		if(SdcardUtil.isSdcardWritable()){
			String path = "/sdcard/"+ctx.getPackageName()+"/crash/";
			Log.d(TAG, "crash path: "+path);
			try {
				File dir = new File(path);
				if (!dir.exists()) {
					dir.mkdirs();
				}
				FileOutputStream fos = new FileOutputStream(path+fileName);
				fos.write(sb.toString().getBytes());
				fos.close();
				Log.d(TAG, "错误日志已保存在路径:"+path+fileName);
				return fileName;
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}


	/**
	 * 将错误日志信息上传到服务器
	 * @param file
	 * @param url
	 */
	private void upLoadCrashInfo2Service(File file,String url){
		
		
	}

}


2、完成这个CrashHandler后,我们需要在一个Application环境中让其运行,为此,我们继承android.app.Application  在子类中<IceApplication>注册初始化CrashHandler。代码如下:

package com.ice.android;

import com.ice.android.common.excepion.CrashHandler;

import android.app.Application;
/**
 * 
 * @author ice
 * 参考博客:http://blog.csdn.net/liuhe688/article/details/6584143
 */
public class IceApplication extends Application {

	@Override
	public void onCreate() {
		super.onCreate();
		// 获得自定义的 未捕获异常处理器
		CrashHandler mCrashHandler = CrashHandler.getInstance();
		mCrashHandler.init(getApplicationContext());
	}	
}

3、最后,为了让我们的IceApplication取代android.app.Application的地位,在我们的代码中生效,我们需要修改AndroidManifest.xml:

<application android:name=".IceApplication" ...>  
</application>  

4、在代码中有遇到异常后要保存设备参数和具体异常信息到sdCard,所以我们需要在AndroidManifest.xml中加入读写SDCARD权限:

<!-- 写入SdCard卡权限  -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

5、运行项目、当应用遇到有未捕获的异常后  看到的效果如下:



同时在SDCARD上有生成的日志信息文件:



这些日志信息文件对于开发者来说是非常有帮助的。


最后感谢博客:http://blog.csdn.net/liuhe688/article/details/6584143 给我带来学习参考的内容。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要捕获 Android 应用程序的异常并重启应用程序,可以使用 Thread.UncaughtExceptionHandler 接口。该接口用于捕获未捕获的异常,并在捕获异常后重启应用程序。 下面是一个简单的示例代码,用于设置应用程序的 UncaughtExceptionHandler: ``` public class MyApplication extends Application implements Thread.UncaughtExceptionHandler { @Override public void onCreate() { super.onCreate(); Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread thread, Throwable ex) { // 捕获异常并重启应用程序 Intent intent = new Intent(getApplicationContext(), MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT); AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, pendingIntent); System.exit(2); } } ``` 在上述示例代码中,我们创建了一个自定义的 Application 类,并实现 Thread.UncaughtExceptionHandler 接口。在 onCreate() 方法中,我们将当前线程的默认 UncaughtExceptionHandler 设置为该应用程序的 UncaughtExceptionHandler。 当应用程序中有未捕获的异常时,会调用 uncaughtException() 方法。在该方法中,我们创建一个 Intent 对象,用于启动 MainActivity,然后使用 PendingIntent 将该 Intent 对象封装为一个闹钟事件,并在 1 秒钟后启动该事件。最后,我们调用 System.exit() 方法退出应用程序。 这样,当应用程序中发生未捕获的异常时,应用程序将自动重启。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值