Android性能優化之ANR異常監測

前言

?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命令將錯誤的日志導出并找到錯誤的類進行分析。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容