Android 应用中未捕获异常的处理
Android 应用开发中常常需要捕获全局异常,以进行一些个性化的处理,如弹出提示,上传错误报告等;
一般的做法是实现 UncaughtExceptionHandler
接口,并重写 uncaughtException()
方法:
public class CrashHandler implements UncaughtExceptionHandler{
private static CrashHandler handler;//单例模式
private Context context;
public static CrashHandler getInstance() {
if (handler == null){
handler = new CrashHandler();
}
return handler;
}
public void init(Context context){
this.context.context;
Thread.setDefaultUncaughtExceptionHandler(this);//设置默认handler
}
@Override
public void uncaughtException(Thread thread, final Throwable error) {
ex.printStackTrace();
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(context, "程序出现异常!", Toast.LENGTH_LONG).show();
//TODO: 上传错误日志等
Looper.loop();
}
}.start();
try {//休眠以等待上面的操作完成,严格地说应该采用synchronized + wait() + notify()实现
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
android.os.Process.killProcess(android.os.Process.myPid());//杀掉当前进程
//System.exit(0);
}
}
上面的代码乍一看没有什么大问题,但是实际开发中会发现,如果发生 crash 的 Activity
不是第一个页面(栈内还有其他 Activity
),则会自动回到之前的页面。而以上操作只是杀掉了当前 Activity
,而且由于很多内容被回收,之前这些页面布局显示会变得混乱,严重影响用户体验。
实际上上面代码的 34 、35 两行代码效果差不多:都是杀掉当前进程,而我们要的效果是杀掉所有相关进程(同一个包下)。
关于 Android 怎么杀掉所有 Activity
,网上的相关文章有不少,例如我从 StackOverflow 上看到的:
Android: How to kill an application with all its activities?
里面大致有三种方案:
重写所有
Activity
的onActivityRsult
方法,在里面调用finish()
;跳转到一指定
Activity
并清除栈顶,在该Activity
的onCreate()
中接收到Intent
后调用finish()
;在每个里面动态注册一个
BroadcastReceiver
,然后通过它去调用finish()
.
前两种没有亲测,第三种测过,结果是不但没能杀掉所有 Activity
,而且引起了ANR!只能另辟蹊径了。
每次系统启动都会开启一个 ActivityManagerService
( AMS )服务,在 AMS 服务中有一个 ActivityStack
实例专门管理手机上的 ActivityRecord
实例,这样 AMS 理论上应该能管理进程。
因此很自然想到 ActivityManager
这个类。看了下 API ,果然有个 killBackgroundProcesses()
方法,而且参数正是包名!果断将上面代码第 35 行改为:
((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
.killBackgroundProcesses(context.getPackageName());
经过测试,部分机型 OK ,但仍有很多机型依旧会重新打开之前页面。但据同事说,这些机型上的其他应用 crash 时不会这样。
会不会是我们的人工干预(捕获异常)出了问题?如果我们不做任何处理会怎么样?
事实发现这样依然会自动重新打开之前页面。那就说明这应该是系统的一个特性,我们还是得自己处理 crash ,而且之前处理的不够。
于是又上 StackOverflow 找答案,这回找到个貌似比较靠谱的,至少标题看起来是这样:
Android App Restarts upon Crash/force close
而且里面一位仁兄的回复很符合我当时的想法:
However, if your app is crashing, then there’s not much you can do about it (apart from periodically saving important stuff to preferences/databases/etc.).
It is probably better to focus on preventing crashes, rather than trying to handle crashes!
呵呵~ 这问题看起来确实无解,可能我们能做的确实只有避免 crash ,而不是怎么优雅的处理 crash 的善后工作。
但是理智地想一想,Android 碎片化如此严重,要想完全避免 crash 那就是痴人说梦!
虽然从老外那里没有找到完美的解决方案,但是突然想到了一种可能的方案:强制跳转到桌面(Home),然后再杀掉当前进程,并 kill 后台进程。
在上面代码的 34 行插入跳转操作:
Intent intent=new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
哈哈,这回亲测基本 OK :跳转到桌面后不会再返回到之前的页面,虽然手动重启时会跳转到 crash 之前的那个页面,但只需退出那个页面并再次启动即可,而不是像之前那样要将栈内的所有历史页面逐个 pop 出来。
虽然还谈不上完美得解决问题,但至少是个不错的折衷方案了。
Okay,先这样吧。