美文网首页
Android性能优化之ANR异常监测

Android性能优化之ANR异常监测

作者: 怪兽来啦啦啦啦啦 | 来源:发表于2020-03-15 20:10 被阅读0次

    前言

     ANR是Application Not Responding的缩写,即应用程序无响应。简单来说,就是应用的界面突然卡住了,无法响应用户的操作如触摸事件等。

    优化思路


    1 ANR执行流程

    • 发生ANR
    • 进程接收异常终止信号,开始写入进程ANR信息
    • 弹出ANR提示框(Rom表现不一,有可能不弹)

    2 如何解决ANR

     解决ANR问题,首先要做的是找到问题,线下我们可以通过ADB命令导出ANR文件进行分析,线上我们可以使用FileObserver或ANR-WatchDog保存ANR堆栈信息,然后上传到服务器。
    2.1导出ANR文件
     ANR发生之后我们可以使用以下命令导出ANR文件:

    adb pull data/anr/traces.txt 存储路径
    

    或者

    //生成
    adb bugreport
    //导出
    adb pull 文件路径 存储路径
    
    生成bugreport
    导出bugreport
    2.2 ANR文件分析
     使用命令会导出一个zip压缩包,ANR文件在FS/data/anr目录下。
    ANR文件
     我的实例是在MainActivity的第41行做了个Thread.sleep(20*1000)的操作。
    ANR文件分析
    2.3 线上ANR监控方案
     线上监控方案我们可以实现FileObserver去监听ANR目录的变化和使用ANR-WatchDod去监听并打印ANR堆栈信息。
    FileObserver
    注意事项:
    • FileOberver需要三种权限
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <!--在sdcard中创建/删除文件的权限 -->
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
    
    • FileOberver的startWatching、stopWatching都加锁,因此我们要避免在对象锁中实现,以免造成死锁。
     public int[] startWatching(List<File> files,
                    @NotifyEventType int mask, FileObserver observer) {
                final int count = files.size();
                final String[] paths = new String[count];
                for (int i = 0; i < count; ++i) {
                    paths[i] = files.get(i).getAbsolutePath();
                }
                final int[] wfds = new int[count];
                Arrays.fill(wfds, -1);
                startWatching(m_fd, paths, mask, wfds);
                final WeakReference<FileObserver> fileObserverWeakReference = new WeakReference<>(observer);
                synchronized (m_observers) {
                    for (int wfd : wfds) {
                        if (wfd >= 0) {
                            m_observers.put(wfd, fileObserverWeakReference);
                        }
                    }
                }
    
                return wfds;
            }
    

     使用方法:

     @SuppressLint("NewApi")
     fun startANRListener(){
          val fileObserver =  object :FileObserver(File("/data/anr/"), CLOSE_WRITE){
              override fun onEvent(event: Int, path: String?) {
                Log.d("ANRWatching", "/data/anr/$path")
              }
          }
            fileObserver.startWatching()
     }
    

    ANR-WatchDog
     Git地址:ANR-WatchDog
     ANR-WatchDog是一个非侵入式的ANR监控组件。
    使用步骤:

    • 在app/build.gradle添加依赖
    implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0'
    
    • 在application类的onCreate中使用,即可实现自动监测ANR。
    class MyApplication: Application() {
        override fun onCreate() {
            super.onCreate()
            ANRWatchDog().start();
        }
    }
    

    ANR发生之后可直接在日志中查看堆栈信息:

     也可以在Application中监听ANR-WatchDog返回的错误日志。

    class MyApplication : Application() {
        override fun onCreate() {
            super.onCreate()
            BlockCanary.install(this, AppBlockCanaryContext()).start()
            val anrWatchDog = ANRWatchDog()
            anrWatchDog.setANRListener {
            }
            anrWatchDog.start()
        }
    }
    

    原理
     ANRWatchDog继承子Thread,所以它最重要的就是run方法。核心内容可以分为以下几点:

    • 在run方法中实现无限循环
    • _tick自动加上5000毫秒并往Handler发消息
    • 睡眠5000毫秒
    • 如果5000毫秒之内主线程还没有处理Runnable,将_tick置0,则进入ANR异常。
    public class ANRWatchDog extends Thread {
        private static final int DEFAULT_ANR_TIMEOUT = 5000;
        private final Handler _uiHandler = new Handler(Looper.getMainLooper());
        private final int _timeoutInterval;
    
        private volatile long _tick = 0;
        private volatile boolean _reported = false;
    
        private final Runnable _ticker = new Runnable() {
            @Override public void run() {
                // _tick = 0用于 第5步判断
                _tick = 0;
                _reported = false;
            }
        };
    
        /**
         * Constructs a watchdog that checks the ui thread every {@value #DEFAULT_ANR_TIMEOUT} milliseconds
         */
        public ANRWatchDog() {
            this(DEFAULT_ANR_TIMEOUT);
        }
    
        /**
         * Constructs a watchdog that checks the ui thread every given interval
         *
         * @param timeoutInterval The interval, in milliseconds, between to checks of the UI thread.
         *                        It is therefore the maximum time the UI may freeze before being reported as ANR.
         */
        public ANRWatchDog(int timeoutInterval) {
            super();
            _timeoutInterval = timeoutInterval;
        }
    
    
    
        @SuppressWarnings("NonAtomicOperationOnVolatileField")
        @Override
        public void run() {
            setName("|ANR-WatchDog|");
    
            long interval = _timeoutInterval;
            //1 无限循环
            while (!isInterrupted()) {
                boolean needPost = _tick == 0;
                //2 _tick自增
                _tick += interval;
                if (needPost) {
                    //3 发送消息
                    _uiHandler.post(_ticker);
                }
    
                try {
                    //4 睡眠
                    Thread.sleep(interval);
                } catch (InterruptedException e) {
                    _interruptionListener.onInterrupted(e);
                    return ;
                }
    
                //5 如果UI线程没有处理_ticker, 走下面代码块,进入ANR异常。
                if (_tick != 0 && !_reported) {
                    //noinspection ConstantConditions
                    if (!_ignoreDebugger && (Debug.isDebuggerConnected() || Debug.waitingForDebugger())) {
                        Log.w("ANRWatchdog", "An ANR was detected but ignored because the debugger is connected (you can prevent this with setIgnoreDebugger(true))");
                        _reported = true;
                        continue ;
                    }
    
                    interval = _anrInterceptor.intercept(_tick);
                    if (interval > 0) {
                        continue;
                    }
    
                    final ANRError error;
                    if (_namePrefix != null) {
                        error = ANRError.New(_tick, _namePrefix, _logThreadsWithoutStackTrace);
                    } else {
                        error = ANRError.NewMainOnly(_tick);
                    }
                    _anrListener.onAppNotResponding(error);
                    interval = _timeoutInterval;
                    _reported = true;
                }
            }
        }
    
    }
    

    总结

     ANR异常我们可分为线上监测和线下监测两个方向

    • 线上监测主要是利用FileObserver进行ANR目录文件变化监听,以ANR-WatchDog进行补充。
    • FileObserver在使用过程中应注意高版本程序不可用以及预防死锁出现。
    • 线下监测主要是在报错之后利用ADB命令将错误的日志导出并找到错误的类进行分析。

    相关文章

      网友评论

          本文标题:Android性能优化之ANR异常监测

          本文链接:https://www.haomeiwen.com/subject/lanashtx.html