Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 全局異常錯誤或崩潰捕捉

Android 全局異常錯誤或崩潰捕捉

編輯:關於Android編程

當出現崩潰,軟件不會閃退,會出現彈出一個對話框,異常錯誤信息會自動保存在sd卡crash這個文件夾下。後續需要還可以發送到服務器的。看效果圖。

1、實現效果圖

\

2、全局異常捕捉類CrashHandler

 

package com.crashhandler.util;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.crashhandler.activity.R;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.view.WindowManager;

/**
 * UncaughtException處理類,當程序發生Uncaught異常的時候,由該類來接管程序,並記錄發送錯誤報告.
 * 
 */
public class CrashHandler implements UncaughtExceptionHandler {
	// 系統默認的UncaughtException處理類
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	// CrashHandler實例
	private static CrashHandler INSTANCE;
	// 程序的Context對象
	private Context mContext;

	//保證只有一個CrashHandler實例 
	private CrashHandler() {

	}

	//獲取CrashHandler實例 ,單例模式
	public static CrashHandler getInstance() {
		if (INSTANCE == null)
			INSTANCE = new CrashHandler();
		return INSTANCE;
	}

	/**
	 * 初始化
	 * 
	 * @param context
	 */
	public void init(Context context) {
		mContext = context;
		// 獲取系統默認的UncaughtException處理器
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		// 設置該CrashHandler為程序的默認處理器
		Thread.setDefaultUncaughtExceptionHandler(this);
	}

	/**
	 * 當UncaughtException發生時會轉入該重寫的方法來處理
	 */
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果自定義的沒有處理則讓系統默認的異常處理器來處理
			mDefaultHandler.uncaughtException(thread, ex);
		}
	}

	/**
	 * 自定義錯誤處理,收集錯誤信息 發送錯誤報告等操作均在此完成.
	 * 
	 * @param ex
	 *            異常信息
	 * @return true 如果處理了該異常信息;否則返回false.
	 */
	public boolean handleException(Throwable ex) {
		if (ex == null || mContext == null)
			return false;
		final String crashReport = getCrashReport(mContext, ex);
		new Thread() {
			public void run() {
				Looper.prepare();
				File file = save2File(crashReport);
				sendAppCrashReport(mContext, crashReport, file);
				Looper.loop();
			}

		}.start();
		return true;
	}

	@SuppressLint(SimpleDateFormat)
	private File save2File(String crashReport) {
		//用於格式化日期,作為日志文件名的一部分
		DateFormat dateFormat = new SimpleDateFormat(yyyyMMddHHmmss);
		String time = dateFormat.format(new Date());
	   	String fileName = crash- + time + - + System.currentTimeMillis() + .txt;
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
			try {
				//存儲路徑,是sd卡的crash文件夾
				File dir = new File(Environment.getExternalStorageDirectory()
						.getAbsolutePath() + File.separator + crash);
				if (!dir.exists())
					dir.mkdir();
				File file = new File(dir, fileName);
				FileOutputStream fos = new FileOutputStream(file);
				fos.write(crashReport.toString().getBytes());
				fos.close();
				return file;
			} catch (Exception e) {
				//sd卡存儲,記得加上權限,不然這裡會拋出異常
				Log.i(Show,save2File error: + e.getMessage());
			}
		}
		return null;
	}

	private void sendAppCrashReport(final Context context,
			final String crashReport, final File file) {
		AlertDialog.Builder builder = new AlertDialog.Builder(context)
		.setIcon(android.R.drawable.ic_dialog_info)
		.setTitle(R.string.app_error)
		.setMessage(R.string.app_error_message)
		.setPositiveButton(R.string.submit_report,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						try {

							//這以下的內容,只做參考,因為沒有服務器
							Intent intent = new Intent(Intent.ACTION_SEND);
							intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
							String[] tos = { [email protected] };
							intent.putExtra(Intent.EXTRA_EMAIL, tos);

							intent.putExtra(Intent.EXTRA_SUBJECT,
									Android客戶端 - 錯誤報告);
							if (file != null) {
								intent.putExtra(Intent.EXTRA_STREAM,
										Uri.fromFile(file));
								intent.putExtra(Intent.EXTRA_TEXT,
										請將此錯誤報告發送給我,以便我盡快修復此問題,謝謝合作!
);
							} else {
								intent.putExtra(Intent.EXTRA_TEXT,
										請將此錯誤報告發送給我,以便我盡快修復此問題,謝謝合作!

												+ crashReport);
							}
							intent.setType(text/plain);
							intent.setType(message/rfc882);
							Intent.createChooser(intent, Choose Email Client);
							context.startActivity(intent);
							
						} catch (Exception e) {
							Log.i(Show,error: + e.getMessage());
						} finally {
							dialog.dismiss();
							// 退出
							android.os.Process.killProcess(android.os.Process.myPid());
							System.exit(1);
						}
					}
				})
		.setNegativeButton(android.R.string.cancel,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 退出
						android.os.Process.killProcess(android.os.Process.myPid());
						System.exit(1);
					}
				});
		
		AlertDialog dialog = builder.create();
		//需要的窗口句柄方式,沒有這句會報錯的
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
		dialog.show();
	}

	/**
	 * 獲取APP崩潰異常報告
	 * 
	 * @param ex
	 * @return
	 */
	private String getCrashReport(Context context, Throwable ex) {
		PackageInfo pinfo = getPackageInfo(context);
		StringBuffer exceptionStr = new StringBuffer();
		exceptionStr.append(Version:  + pinfo.versionName + (
				+ pinfo.versionCode + )
);
		exceptionStr.append(Android:  + android.os.Build.VERSION.RELEASE
				+ ( + android.os.Build.MODEL + )
);
		exceptionStr.append(Exception:  + ex.getMessage() + 
);
		StackTraceElement[] elements = ex.getStackTrace();
		for (int i = 0; i < elements.length; i++) {
			exceptionStr.append(elements[i].toString() + 
);
		}
		return exceptionStr.toString();
	}

	/**
	 * 獲取App安裝包信息
	 * 
	 * @return
	 */
	private PackageInfo getPackageInfo(Context context) {
		PackageInfo info = null;
		try {
			info = context.getPackageManager().getPackageInfo(
					context.getPackageName(), 0);
		} catch (NameNotFoundException e) {
			 e.printStackTrace(System.err);
		}
		if (info == null)
			info = new PackageInfo();
		return info;
	}

}

 

3、系統級MyApplication繼承Application。因為需要整個軟件的全局,所以需要在這裡初始化CrashHandler

 

 

package com.crashhandler.util;
import android.app.Application;
public class MyApplication extends Application{
	private static MyApplication mApplication;
	
	public synchronized static MyApplication getInstance() {
		return mApplication;
	}
	
	@Override
	public void onCreate() {
		super.onCreate();
		initData();
	}

	private void initData() {
		//當程序發生Uncaught異常的時候,由該類來接管程序,一定要在這裡初始化
		CrashHandler.getInstance().init(this);
	}

}

 

4、看怎麼使用MainActivity

 

 

package com.crashhandler.activity;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Button button = (Button)findViewById(R.id.button1);
		button.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.button1:
			//自已寫了一個異常信息,進行測試
			Button button = (Button)findViewById(R.id.textView1);
			break;

		default:
			break;
		}
		
	}
}

 

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved