自己定义Application的未捕获异常处理

Posted blfbuaa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己定义Application的未捕获异常处理相关的知识,希望对你有一定的参考价值。

    近期由于工作原因。进行android应用开发时发现应用在出现类似空指针等异常时,抛出未被捕获的异常。Android系统有默认的未捕获异常处理器,默认行为是结束对应的线程,但并不会直接退出程序,并且在应用还有后台Service时。服务还一直在执行,假设service在请求网络时还会抛出一些异常信息,并且在未全然退出的应用中再次使用还会进一步导致异常,这样对于用户体验来说实在不好。

    因此,须要在应用无论什么情况下出现异常后应该直接退出应用,然后重新启动应用。网上搜了非常多资料。非常多都不能非常好解决问题,由于Android可能会启动非常多Activity,而在结束应用进程之前应该finish全部Task中的Activity,否则会出现一些不想要的结果,当然还要结束后台的Service。

    Android中有一个Thread.UncaughtExceptionHandler接口,须要实现该接口。并实现当中的uncaughtException函数。以下直接贴上相关代码:

public class MyCrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG = "MyCaughtException";
// 应用
private MyApplication mApplication;
// 系统默认的未捕捉异常处理器
private Thread.UncaughtExceptionHandler mDefaultHandler;
// 自己定义的未捕捉异常处理器
private static MyCrashHandler gCaughtException;

/**
* 单例模式的保障
*/
private CEPM360CrashHandler() { 

}

/**
* 单例模式
* @param application
* @return
*/
    public static synchronized MyCrashHandler getInstance(Application application) {
        if (gCaughtException == null) {
        gCaughtException = new MyCrashHandler(application);
        }
        return gCaughtException;
    }

    /**
     * 构造函数
     * @param application
     */
private MyCrashHandler(Application application) {
mApplication = (MyApplication) application;
// 获取系统默认的UncaughtExceptionHandler
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置异常处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}


/**
* 未捕获异常处理函数
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e(TAG, "Ocurrs uncaughtException!");
// 打印堆栈
               ex.printStackTrace();

Intent intent = new Intent(mApplication, MyService.class);
if (!handleException(ex) && mDefaultHandler != null) {
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
mApplication.stopService(intent);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

        // 退出程序后,启动应用第一个LoginActivity
        intent = new Intent(mApplication, MainActivity.class);
        PendingIntent restartIntent = 
        PendingIntent.getActivity(mApplication,
        0, 
        intent, 
        Intent.FLAG_ACTIVITY_NEW_TASK);
        AlarmManager alarmManager = (AlarmManager) 
        mApplication.getSystemService(Context.ALARM_SERVICE);
        alarmManager.set( AlarmManager.RTC, 
        System.currentTimeMillis() + 1000, 
        restartIntent);
        
        mApplication.exit();
}

/**
* 异常处理函数
* @param ex
* @return
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}

// 重新启动一个线程显示异常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
showExceptionToast();
Looper.loop();
}
}.start();
return true;
}

/**
* 显示发生异常Toast,确认后重新启动应用
*/
private void showExceptionToast() {
Toast toast = Toast.makeText(mApplication, 
"非常抱歉,“CEPM360”已停止执行,即将重新启动", Toast.LENGTH_SHORT);
// 设置居中显示
toast.setGravity(Gravity.CENTER, 0, 0);

LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setLayoutDirection(LinearLayout.HORIZONTAL);

ImageView image = new ImageView(mApplication);
image.setImageResource(R.drawable.exception_picture);
toastLayout.addView(image, 0);

toast.setView(toastLayout);
toast.show();
}
}

    上面是自己定义的未捕获异常处理器。要想让其发挥作用,须要配合Application,默认情况下Android不须要你手动实现或实例化一个APPlication对象。但自己定义未捕获异常处理中要finish全部的activity。但在application中没有对应的Task相关API,就没有办法拿到当前应用在Task中的全部Activity,因此须要我们自己实现一个Application类,并维护一个Activity列表,当启动一个Activity时将其增加该列表,当出现未捕获异常时,会逐一finish掉这里的全部Activity。

public class MyApplication extends Application {

// Activity列表,用来全然退出应用
private List<Activity> mActivities = new ArrayList<Activity>();
// 共享数据
private Map<String, Object> mAppSharedData = new HashMap<String, Object>();
    
@Override
public void onCreate() {
super.onCreate();
MyCrashHandler.getInstance(this);
}

/**
* 获取应该共享数据
* @return
*/
public Map<String, Object> getAppSharedData() {
return mAppSharedData;
}

/**
* 加入一个共享Map元素
* @param map
*/
public void addSharedData(Map<String, Object> map) {
mAppSharedData.putAll(map);
}

/**
* 加入一个共享元素
* @param string
* @param object
*/
public void addSharedData(String string, Object object) {
mAppSharedData.put(string, object);
}

/**
* 获取共享元素值
* @param string
* @return
*/
public Object getValue(String string) {
return mAppSharedData.get(string);
}

/**
* 当启动一个activity时,加入到该列表中
* @param activity
*/
public void addActivity(Activity activity) {
mActivities.add(activity);
}

/**
* 退出应用
*/
    public void exit() {
    // 循环退出Activity
        try {
            for (Activity activity : mActivities) {
                if (activity != null)
                    activity.finish();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        // 最后退出虚拟机
            System.exit(0);
        }
    }
    
    @Override
    public void onLowMemory() {
        super.onLowMemory();    
        System.gc();
    }

}

    上面的mAppSharedData映射表是用于应用的数据共享,不是本文讨论重点。这里重点指出:在MyCrashHandler 中的uncaughtException函数中完毕异常的全部处理。包含停止Service和Activity。在发送重新启动广播之后,退出虚拟机。这里也能够使用以下的代码:

 android.os.Process.killProcess(android.os.Process.myPid());






































































































































































































以上是关于自己定义Application的未捕获异常处理的主要内容,如果未能解决你的问题,请参考以下文章

java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理

由于 iOS 中未捕获的异常,架构 i386 和终止应用程序的未定义符号

从 Node.JS 中的未捕获异常中恢复

从 Node.JS 中的未捕获异常中恢复

由于名称为“OverlayView”的未捕获异常“NSInternalInconsistencyException”而终止应用程序

由于使用 AWS iOS 开发工具包的未捕获异常“NSInvalidArgumentException”而终止应用程序