文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android System crash DeadSystemException(Service/Activity/终极解决方案)

2023-09-07 14:08

关注

DeadSystemException

The core Android system has died and is going through a runtime restart. All running apps will be promptly killed.
Android 核心系统服务已经死亡,正在重启中。全部正在运行的app即将被kill杀死。

更多请阅读,DeadSystemException官方介绍

chatgpt给出的解决答案
Android中的DeadSystemException是一种非常严重的异常,表示系统已经崩溃。如果不进行处理,应用程序将会崩溃并退出。

处理方式:

  1. 捕获该异常:使用try-catch来捕获DeadSystemException异常,或者注册一个UncaughtExceptionHandler来处理未捕获的异常
  2. 根据产品需求进行业务逻辑处理:进行相应的处理,例如输出错误日志或者提示用户重新启动应用程序。

1.service create 发生该异常:

在这里插入图片描述
先来看下调用栈中抛出异常的地方:
/frameworks/base/core/java/android/app/ActivityThread.java 中handleCreateService():
在这里插入图片描述
在这里插入图片描述
从源码可知 :

  1. app 进程已经完成对service#oncreate()过程,接着与ams 通过binder通讯,发生了DeadObjectException 从而抛出了DeadSystemException。
  2. ActivityThread 中对service创建过程有异常处理机制try-catch ,会判断Instrumentation 是否拦截该异常。

为了进一步了解原因,接着查看bugly上该异常中捕获的日志,发现有几条有效日志:

//系统服务已经die死亡AndroidRuntime: FATAL EXCEPTION: mainAndroidRuntime: Process: com.android.systemui, PID: 19210AndroidRuntime: DeadSystemException: The system died; earlier logs will point to the root causeAppErrors: Process com.android.systemui has crashed too many times: killing!//binder 通讯异常抛出日志JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 104)

根据分析,推断系统服务已经死亡,app进程通过binder与之通讯,会抛出DeadSystemException。

根据FAILED BINDER TRANSACTION检索,找/frameworks/base/core/jni/android_util_Binder.cpp
signalExceptionForError():
在这里插入图片描述
从binder通讯源码中可知,当遇到异常返回status(failed_transansaction 和dead_object)时, 会抛出DeadObjectException异常。另外,单次读写超过200k的binder数据,就会抛出TransactionTooLargeException 异常;

原因推断:当app进程创建service 成功后,与ams 通讯过程中,binder 发生异常状态status failed_transansaction,从而抛出异常导致。

解决方案:
采用hook 方式,代理Instrumentation 捕获该异常:

     private static class InstrumentationImpl extends Instrumentation{        @Override        public boolean onException(Object obj, Throwable e) {             return isInterruptException(e);        }        public static void hookInstrumentation(){            //基本上发生在7.0和7.1设备上            if (Build.VERSION.SDK_INT==Build.VERSION_CODES.N_MR1||Build.VERSION.SDK_INT==Build.VERSION_CODES.N){                try {                    Instrumentation ins = new InstrumentationImpl();                    Class cls = Class.forName("android.app.ActivityThread");                    Method mthd = cls.getDeclaredMethod("currentActivityThread", (Class[]) null);                    Object currentAT = mthd.invoke(null, (Object[]) null);                    Field mInstrumentation = currentAT.getClass().getDeclaredField("mInstrumentation");                    mInstrumentation.setAccessible(true);                    mInstrumentation.set(currentAT, ins);                }catch (Exception e){                    e.printStackTrace();                }            }        }    }}private static boolean isInterruptException(Throwable e){    if (e.toString().contains("DeadSystemException")){        //拦截DeadSystemException        return true;    }     return false;}

以上代码,在Application的oncreate()中调用。

还有其他hook 方式,ActivityThread#H 的handler callBack ,也可以通用实现以上效果;

捕获该异常后,需要启动的服务无法启动,Bugly 不会捕捉到该异常,但app进程很可能会被杀死。

2.activity destory 发生该异常

先查看调用栈:
在这里插入图片描述
查看下crash 所在的源码:
在这里插入图片描述
与前面的问题有些不同,当app 进程已经完成Activity销毁过程后,通知ams 过程中,发生系统进程,死亡异常则直接抛出。

解决方案
App 销毁Activity过程中没有异常捕捉机制,但该调用栈是在ActivityThread 中名为H 的handler中调用的,因此考虑hook 进行异常捕获。

再查看Handler的源码,发现可以为它设置callBack便可以拦截其handleMessage()调用。

在这里插入图片描述
编写代码如下所示:

private static class  CallBackImpl implements Handler.Callback{    private  Handler activityThreadHandler;    public CallBackImpl(Handler activityThreadHandler) {        this.activityThreadHandler = activityThreadHandler;    }    @Override    public boolean handleMessage(Message msg) {        try {            activityThreadHandler.handleMessage(msg);         }catch (Exception e){            if (!isInterruptException(e)){                //不被拦截的异常,继续抛出                throw  e;             }           }        return true;    }    public static void hook(){        try {            if (Build.VERSION.SDK_INT==Build.VERSION_CODES.N_MR1||Build.VERSION.SDK_INT==Build.VERSION_CODES.N){            //获取到ActivityThread            Class<?> ActivityThreadClass = Class.forName("android.app.ActivityThread");            Field sCurrentActivityThreadField = ActivityThreadClass.getDeclaredField("sCurrentActivityThread");            sCurrentActivityThreadField.setAccessible(true);            Object ActivityThread = sCurrentActivityThreadField.get(null);            //获取到ActivityThread中的handler            Field mHField=ActivityThreadClass.getDeclaredField("mH");            mHField.setAccessible(true);            Handler mHandler=(Handler) mHField.get(ActivityThread);            //给handler添加callback监听器,拦截            Field mCallBackField = Handler.class.getDeclaredField("mCallback");            mCallBackField.setAccessible(true);            mCallBackField.set(mHandler, new CallBackImpl(mHandler));            }        }catch (Exception e){            e.printStackTrace();        }    }}private static boolean isInterruptException(Throwable e){    if (e.toString().contains("DeadSystemException")){        //拦截DeadSystemException        return true;    }     return false;}

以上代码,在Application的oncreate()中调用

3.activity stop发生该异常

在这里插入图片描述
查看源码:
在这里插入图片描述
StopInfo 这个任务是在ActivityThread 执行Activity onstop状态时添加的:
在这里插入图片描述
handleStopActivity() 是在ActivityThread中H的handler中调用的。

解决方案
因StopInfo是runnable接口,无法通过Handler的handleMessage()函数进行拦截,因此考虑直接代理H 这个Handler , 通过dispatchMessage()分发,传递给H处理,进行异常捕捉。

private static class HandlerImpl extends Handler {    private Handler proxy;    public HandlerImpl(Handler proxy) {        super(proxy.getLooper());        this.proxy = proxy;    }    @Override    public void dispatchMessage(@NonNull Message msg) {        try {            proxy.dispatchMessage(msg);        } catch (Exception e) {            if (!isInterruptException(e)) {                //不被拦截的异常,继续抛出                throw e;            }        }    }    public static void hook() {        try {            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1 || Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {                //获取到ActivityThread                Class<?> ActivityThreadClass = Class.forName("android.app.ActivityThread");                Field sCurrentActivityThreadField = ActivityThreadClass.getDeclaredField("sCurrentActivityThread");                sCurrentActivityThreadField.setAccessible(true);                Object ActivityThread = sCurrentActivityThreadField.get(null);                //获取到ActivityThread中的handler                Field mHField = ActivityThreadClass.getDeclaredField("mH");                mHField.setAccessible(true);                Handler mHandler = (Handler) mHField.get(ActivityThread);                mHField.set(ActivityThread, new HandlerImpl(mHandler));            }        } catch (Exception e) {            e.printStackTrace();        }    }}private static boolean isInterruptException(Throwable e) {    if (e.toString().contains("DeadSystemException")) {        //拦截DeadSystemException        return true;    }    return false;}

Hook H 这个Handler 可以解决Activity或者Service 生命周期中该异常的捕获。在Application的oncreate()中调用。

4. (终极解决方案,捕获任何地方发生该问题的调用栈)

实际上可能其他的线程或者其他的handler 中调用跨进程有关的api都可能会遇到该问题,单纯hook ActivityThread中Handler是无法解决的。
终极解决方案:通过主线程的异常处理器,捕获该异常,自定义Thread.UncaughtExceptionHandler。

private static class ExceptionHandlerImpl implements Thread.UncaughtExceptionHandler{    private final Thread.UncaughtExceptionHandler mOldHandler;    public ExceptionHandlerImpl(Thread.UncaughtExceptionHandler mOldHandler) {        this.mOldHandler = mOldHandler;    }    @Override    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {        if (isInterruptException( e)) {            // 异常逻辑 1: 继续执行,进程不结束            resumeMainThreadLoop();            // restartApp() 或者直接中断该进程,进行重启重启该app 的逻辑2            return;        }        if (mOldHandler != null) {            mOldHandler.uncaughtException(t, e);        }    }    private void restartApp(Context context){        // 重新启动应用程序或者系统服务        final Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);        alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pendingIntent);        // 退出应用程序或者停止服务        System.exit(0);    }    private void resumeMainThreadLoop() {        if (Looper.myLooper() != Looper.getMainLooper()) {             return;        }        try {            Looper.loop();        } catch (Exception e) {            uncaughtException(Thread.currentThread(), e);        }    }    public static void init(){            //在bugly初始化或者自定义crash上报组件之后调用            Thread.UncaughtExceptionHandler mOldHandler = Thread.getDefaultUncaughtExceptionHandler();            if (!(mOldHandler instanceof ExceptionHandlerImpl)) {                Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandlerImpl(mOldHandler));            }    }}

以上代码在bugly或者异常捕获组件之后调用。

来源地址:https://blog.csdn.net/hexingen/article/details/130553448

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯