Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android官方開發文檔Training系列課程中文版:如何避免ANR?

Android官方開發文檔Training系列課程中文版:如何避免ANR?

編輯:關於Android編程

盡管你寫代碼可能通過了世界上所有的性能測試,但是它還是可能會讓人感覺到卡頓。當應用卡的不成樣子時,系統會給你彈一個”Application Not Responding”的對話框。

在Android中,系統會對那些長時間沒有響應的應用采取一些措施:彈出一個對話框告訴用戶APP已經停止了響應,如下圖所示:
\

正出於這個原因,系統會在APP長時間沒有響應的時候為用戶提供一個退出APP的選項。所以使APP能夠及時響應這一點是至關重要的,這樣系統才不會向用戶顯示ANR對話框。

這節課我們會學習Android系統如何檢測應用程序是否是未響應,以及應用程序如何保持響應能力的一些改進措施。

什麼觸發了ANR?

通常情況下,系統會在應用程序不再能夠響應用戶的輸入時顯示ANR對話框。比如,如果應用阻塞在了UI線程的IO操作上,那麼系統就不能夠處理用戶的輸入事件。或者應用花費了大量的時間在內存模型的構建上或者是在UI線程中計算了游戲的下一步動作。要記住:
即便是最高效的代碼也需要花費時間來運行。

任何情況下都不要在UI線程中執行耗時操作,而是要將這些工作放在一個單獨的線程中執行。這可以使UI線程保持流暢工作(UI線程負責驅動用戶界面的事件循環)。

在Android中,應用程序的響應態由Activity Manager及Window Manager負責監控。系統會在偵測到以下狀況時顯示ANR對話框:

對輸入事件在5秒內沒有作出響應。 BroadcastReceiver在10秒內沒有執行完畢。

如何避免ANR?

Android應用程序默認運行在UI線程中。這意味著在UI線程中的任何耗時操作都會引發ANR問題,因為這會使應用程序給不到輸入事件或者意圖廣播處理的機會。

因此,在UI線程中的每個方法都應當做盡可能少的工作,尤其是Activity的生命周期回調函數。像網絡或數據庫操作,或者大量的計算之類的耗時操作應當在工作線程中執行。

創建用於執行耗時操作的線程最便捷的方式莫過於使用AsyncTask了。只需要繼承AsyncTask,然後重寫doInBackground()就可以執行了。如果要向用戶展示工作進度,你可以使用publishProgress()方法,它會回調onProgressUpdate()方法(該方法運行於UI線程)。
在onProgressUpdate()內我們可以更新進度條。

private class DownloadFilesTask extends AsyncTask {
    // Do the long-running work in here
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }
    // This is called each time you call publishProgress()
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
    // This is called when doInBackground() is finished
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

使用execute()方法啟動工作線程:

new DownloadFilesTask().execute(url1, url2, url3);

如果不采用這種方式,我們還有另一種實現方法:創建自己的Thread或HandlerThread。如果采用這種方法,那麼應該設置該線程的優先級為”background”:通過Process.setThreadPriority()方法及參數THREAD_PRIORITY_BACKGROUND設置。
如果沒有設置該優先級,那麼該線程會使應用感到變慢,因為該線程的優先級默認與UI線程的優先級一致,它們會互相搶占CPU資源。

如果實現了自己的Thread或HandlerThread,那麼要確保在等待其它工作線程完成之前UI線程不被阻塞—不要調用Thread.wait()或Thread.sleep()。如果需要等待其它線程的執行結果,可以為UI線程創建一個Handler。這樣做可以使UI線程還可以對
輸入事件保持響應能力。這樣就可以避免5秒內無響應的ANR對話框出現。

BroadcastReceiver在執行時間上有特殊的限制,這意味著在其內部的工作一定是輕量級的:比如在後台做一些保存設置或者發送通知的工作。所以與UI線程中執行的方法一樣,廣播接收器內也應當杜絕耗時操作的出現。

TIP: 你可以使用StrictMode來發現UI線程中意外出現的耗時操作。

ANR相關優化

一般來說,100~200毫秒是用戶所能感知到應用卡頓的極限。下面列出了一些可以避免應用程序ANR的一些點,同樣也有助於防止出現卡頓的情況:

如果應用需要對用戶輸入做大量的後台工作,可以顯示一個進度表示工作正在進行。 對於游戲類的復雜計算,應該將這些工作放在工作線程中執行。 如果應用在初始化階段需要花費一些時間,可以考慮顯示一個閃屏頁面或者盡可能快的顯示主界面:展示加載正在進行,並進行異步數據填充。在這些情況下都應當表明任務正在進行,以免讓用戶認為應用已經卡死。 使用Systrace或Traceview等性能工具檢查APP的響應瓶頸。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved