作为一名android开发人员,开发好的应用推广出去的话,很难预料到会有哪些异常。
而那些未被捕获的异常如果经常出现的话,会很影响用户体验效果的。
android其实对于这些未知的异常,有一个捕获机制的。
好了,废话不多上,上一个例子。
首先是一个异常处理类:
public class ExceptionHandler implements UncaughtExceptionHandler {
/** Debug Log tag */
public static final String TAG = "CrashHandler";
/**
* 是否开启日志输出,在Debug状态下开启, 在Release状态下关闭以提示程序性能
* */
public static final boolean DEBUG = true;
/** 系统默认的UncaughtException处理类 */
private Thread.UncaughtExceptionHandler mDefaultHandler;
/** CrashHandler实例 */
private static ExceptionHandler 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 OS_VER= "osVer";//安卓版本
private static final String Device_TYPE = "deviceType";//手机型号
private static final String STACK_TRACE = "STACK_TRACE";
/** 错误报告文件的扩展名 */
private static final String CRASH_REPORTER_EXTENSION = ".cr";
/** 保证只有一个CrashHandler实例 */
private ExceptionHandler() {
}
/** 获取CrashHandler实例 ,单例模式 */
public static ExceptionHandler getInstance() {
if (instance == null) {
instance = new ExceptionHandler();
}
return instance;
}
/**
* 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器
* @param ctx
*/
public void init(Context ctx) {
mContext = ctx;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 当UncaughtException发生时会转入该函数来处理
*/
boolean flag=true;
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if(flag==false)return;
flag=false;
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
Log.e("TEST", "handle Throwable");
} else {
// Sleep一会后结束程序
Log.e("TEST", "sleep and finish");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, "Error : ", e);
}
Log.e("TEST", "异常");
}
// mDefaultHandler.uncaughtException(thread, ex);
SPZApplication.getInstance().exit();
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
* @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() {
Looper.prepare();
Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出。\n"+msg, Toast.LENGTH_LONG)
.show();
Looper.loop();
}
}.start();
// 收集设备信息
collectCrashDeviceInfo(mContext);
// 保存错误报告文件
saveCrashInfoToFile(ex);
// 发送错误报告到服务器
return true;
}
/**
* 保存错误信息到文件中
*
* @param ex
* @return
*/
private void 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(STACK_TRACE, result);
try {
long timestamp = System.currentTimeMillis();
String fileName = "crash-" + timestamp + CRASH_REPORTER_EXTENSION;
File file = new File(Constant.StorageLocation_CRASH+File.separator+fileName);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
FileOutputStream trace = new FileOutputStream(file);
mDeviceCrashInfo.store(trace, "");
trace.flush();
trace.close();
} catch (Exception e) {
Log.e(TAG, "an error occured while writing report file...", e);
}
}
/**
* 收集程序崩溃的设备信息
* @param ctx
*/
public void collectCrashDeviceInfo(Context ctx) {
PackageInfo versionInfo = Utils.getVersionInfo(ctx);
mDeviceCrashInfo.put(VERSION_NAME, new String(versionInfo.versionName).trim());
mDeviceCrashInfo.put(VERSION_CODE, new String(Integer.toString(versionInfo.versionCode)).trim());
mDeviceCrashInfo.put(OS_VER, new String(Build.VERSION.RELEASE).trim());
mDeviceCrashInfo.put(Device_TYPE, new String(Build.MODEL).trim());
}
}
如果想全局的接收异常,每个应用对应唯一的Application,我们就应该注册这个application的UncaughtExceptionHandler。
SPZApplication
Application里面有一个List<Activity>,是存储用来结束所有的Activity的,也用过System.exit(1);等等方式,都不理想,这些方法只能结束一个Activity
public class SPZApplication extends Application {
private List<Activity> activityList = new LinkedList<Activity>();
private static SPZApplication instance;
// 单例模式中获取唯一的MyApplication实例
public static SPZApplication getInstance() {
return instance;
}
// 添加Activity到容器中
public void addActivity(Activity activity) {
activityList.add(activity);
}
// 遍历所有Activity并finish
public void exit() {
for (Activity activity : activityList) {
if(!activity.isFinishing()){
activity.finish();
}
}
}
@Override
public void onCreate() {
super.onCreate();
instance=this;
ExceptionHandler exceptionHandler = ExceptionHandler.getInstance();
// 注册crashHandler
exceptionHandler.init(getApplicationContext());
// 发送以前没发送的报告(可选)
// exceptionHandler.sendPreviousReportsToServer();
}
}
manifest.xml中的注册:
<application
android:allowBackup="true"
android:icon="@drawable/showicon"
android:label="@string/app_name"
android:name="com.lock.activity.SPZApplication"
android:theme="@style/AppTheme" >
<activity
android:name="com.bpo.lock.activity.CheckActivity"
android:label="@string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>