04.Android崩溃Crash库之Loop拦截崩溃和ANR

简介: 04.Android崩溃Crash库之Loop拦截崩溃和ANR

目录总结

  • 01.能否利用Looper拦截崩溃
  • 02.思考几个问题分析
  • 03.App启动时自动开启Looper
  • 04.拦截主进程崩溃

前沿

01.能否利用Looper拦截崩溃

  • 问题思考一下

    • 能否基于 Handler 和 Looper 拦截全局崩溃(主线程),避免 APP 退出。
    • 能否基于 Handler 和 Looper 实现 ANR 监控。
  • 测试代码如下所示

    public class App extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            CrashTestDemo.test();
        }
    }
    
    //测试代码
    public class CrashTestDemo {
    
        private static long startWorkTimeMillis = 0L;
        public static void test(){
            Looper.getMainLooper().setMessageLogging(new Printer() {
                @Override
                public void println(String it) {
                    if (it.startsWith(">>>>> Dispatching to Handler")) {
                        startWorkTimeMillis = System.currentTimeMillis();
                    } else if (it.startsWith("<<<<< Finished to Handler")) {
                        long duration = System.currentTimeMillis() - startWorkTimeMillis;
                        if (duration > 100) {
                            Log.e("Application---主线程执行耗时过长","$duration 毫秒,$it");
                        }
                    }
                }
            });
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    while (true){
                        try {
                            Looper.loop();
                        } catch (Throwable e){
                            if (e.getMessage()!=null && e.getMessage().startsWith("Unable to start activity")){
                                android.os.Process.killProcess(android.os.Process.myPid());
                                break;
                            }
                            e.printStackTrace();
                            Log.e("Application---Looper---",e.getMessage());
                        }
                    }
                }
            });
            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    e.printStackTrace();
                    Log.e("Application-----","uncaughtException---异步线程崩溃,自行上报崩溃信息");
                }
            });
        }
    }
  • 通过上面的代码就可以就可以实现拦截UI线程的崩溃,耗时性能监控。但是也并不能够拦截所有的异常,如果在Activity的onCreate出现崩溃,导致Activity创建失败,那么就会显示黑屏。

02.思考几个问题分析

  • 通过上面简单的代码,我们就实现崩溃和ANR的拦截和监控,但是我们可能并不知道是为何实现的,包括我们知道出现了ANR,但是我们还需要进一步分析为何处出现ANR,如何解决。
  • 今天分析的问题有:

    • 如何拦截全局崩溃,避免APP退出。如何实现 ANR 监控。拦截到了之后可以做什么处理,如何优化?

03.App启动时自动开启Looper

  • 先从APP启动开始分析,APP的启动方法是在ActivityThread中,在main方法中创建了主线程的Looper,也就是当前进程创建。

    • 在main方法的最后调用了 Looper.loop(),在这个方法中处理主线程的任务调度,一旦执行完这个方法就意味着APP被退出了。
    • 如果我们要避免APP被退出,就必须让APP持续执行Looper.loop()。注意这句话非常重要!!!
    public final class ActivityThread extends ClientTransactionHandler {
        ...
        public static void main(String[] args) {
            ...
            Looper.prepareMainLooper();
            ...
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    }
  • 那进一步分析Looper.loop()方法

    • 在这个方法中写了一个循环,只有当 queue.next() == null 的时候才退出,看到这里我们心里可能会有一个疑问,如果没有主线程任务,是不是Looper.loop()方法就退出了呢?
    • 实际上queue.next()其实就是一个阻塞的方法,如果没有任务或没有主动退出,会一直在阻塞,一直等待主线程任务添加进来。
    • 当队列有任务,就会打印信息 Dispatching to ...,然后就调用 msg.target.dispatchMessage(msg);执行任务,执行完毕就会打印信息 Finished to ...,我们就可以通过打印的信息来分析 ANR,一旦执行任务超过5秒就会触发系统提示ANR,但是我们对自己的APP肯定要更加严格,我们可以给我们设定一个目标,超过指定的时长就上报统计,帮助我们进行优化。
    public final class Looper {
        final MessageQueue mQueue;
        public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                // This must be in a local variable, in case a UI event sets the logger
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
                }
                try {
                    msg.target.dispatchMessage(msg);
                } finally {}
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
                msg.recycleUnchecked();
            }
        }
        public void quit() {
            mQueue.quit(false);
        }
    }
  • 如何让app崩溃后不会退出

    • 如果主线程发生了异常,就会退出循环,意味着APP崩溃,所以我们我们需要进行try-catch,避免APP退出,我们可以在主线程再启动一个 Looper.loop() 去执行主线程任务,然后try-catch这个Looper.loop()方法,就不会退出。

04.拦截主进程崩溃

  • 拦截主进程崩溃其实也有一定的弊端,因为给用户的感觉是点击没有反应,因为崩溃已经被拦截了。如果是Activity.create崩溃,会出现黑屏问题,所以如果Activity.create崩溃,必须杀死进程,让APP重启,避免出现改问题。

    public class MyApplication extends Application {
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            new Handler(getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Looper.loop();
                        } catch (Throwable e) {
                            e.printStackTrace();
                            // TODO 需要手动上报错误到异常管理平台,比如bugly,及时追踪问题所在。
                            if (e.getMessage() != null && e.getMessage().startsWith("Unable to start activity")) {
                                // 如果打开Activity崩溃,就杀死进程,让APP重启。
                                android.os.Process.killProcess(android.os.Process.myPid());
                                break;
                            }
                        }
                    }
                }
            });
        }
    }

项目地址:https://github.com/yangchong211/YCAndroidTool

目录
相关文章
|
20天前
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
47 8
|
25天前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
22 1
|
29天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
81 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
1月前
|
Ubuntu Shell API
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
|
3月前
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
307 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
3月前
|
监控 Java API
Android经典实战之OkDownload:一个经典强大的文件下载开源库,支持断点续传
本文介绍的 OkDownload 是一个专为 Android 设计的开源下载框架,支持多线程下载、断点续传和任务队列管理等功能,具备可靠性、灵活性和高性能特点。它提供了多种配置选项和监听器,便于开发者集成和扩展。尽管已多年未更新,但依然适用于大多数文件下载需求。
350 1
|
3月前
|
API Android开发
Android项目架构设计问题之选择和使用合适的UI库如何解决
Android项目架构设计问题之选择和使用合适的UI库如何解决
48 0
|
3月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
很多文章都介绍了FTPClient如何连接ftp服务器,但却很少有人说如何连接一台开了SSL认证的ftp服务器,现在代码来了。
100 2
|
4月前
|
消息中间件 Android开发 开发者
🔍深度剖析Android内存泄漏,让你的App远离崩溃边缘,稳如老狗!🐶
【7月更文挑战第28天】在 Android 开发中,内存管理至关重要。内存泄漏可悄无声息地累积,最终导致应用崩溃或性能下滑。它通常由不正确地持有 Activity 或 Fragment 的引用引起。常见原因包括静态变量持有组件引用、非静态内部类误用、Handler 使用不当、资源未关闭及集合对象未清理。使用 Android Studio Profiler 和 LeakCanary 可检测泄漏,修复方法涉及使用弱引用、改用静态内部类、妥善管理 Handler 和及时释放资源。良好的内存管理是保证应用稳定性的基石。
78 4
|
3月前
|
测试技术 API Android开发
Android经典实战之简化 Android 相机开发:CameraX 库的全面解析
CameraX是Android Jetpack的一个组件,旨在简化相机应用开发,提供了易于使用的API并支持从Android 5.0(API级别21)起的设备。其主要特性包括广泛的设备兼容性、简洁的API、生命周期感知、简化实现及方便的集成与测试。通过简单的几个步骤即可实现如拍照、视频录制等功能。此外,还提供了最佳实践指导以确保应用的稳定性和性能。
64 0