天天看點

Android-ANR

參考:

如何分析解決Android ANR

App性能優化系列6-ANR詳解

一:什麼是ANR

ANR : Application Not Responding,即應用無響應。

二:ANR的類型

ANR一般有三種類型:

1:KeyDispatch Timeout(5 seconds) –主要類型

按鍵或觸摸事件 在特定時間内無響應

2:Broadcast Timeout(10 seconds)

BroadcastReceiver 在特定時間内無法處理完成

3:Service Timeout(20 seconds) –小機率類型

Service 在特定的時間内無法處理完成

三:為什麼會逾時呢?

ANR首要原因就是 在主線程(UI線程)裡面做了太多的阻塞耗時操作 , 例如檔案讀寫, 資料庫讀寫, 網絡查詢等等.

逾時時間的計數一般是從按鍵分發給app開始。逾時的原因一般有兩種:

(1)目前的事件沒有機會得到處理(即UI線程正在處理前一個事件,沒有及時的完成或者looper被某種原因阻塞住了)

(2)目前的事件正在處理,但沒有及時完成。

四:如何避免KeyDispatchTimeout

知道了ANR産生的原因, 那麼想要避免ANR, 也就很簡單了, 就一條規則:

不要在主線程(UI線程)裡面做繁重的操作.

1:UI線程盡量隻做跟UI相關的工作

2:耗時的工作(比如資料庫操作,I/O,連接配接網絡或者别的有可能阻礙UI線程的操作)把它放入單獨的線程處理

3:盡量用Handler來處理UIthread和别的thread之間的互動。

如何避免ANR

1.使用AsyncTask處理耗時IO操作。

2.使用Thread或者HandlerThread時,調用

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
           

設定優先級,否則仍然會降低程式響應,因為預設Thread的優先級和主線程相同。

3.使用Handler處理工作線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程。

4.Activity的onCreate和onResume回調中盡量避免耗時的代碼

5.BroadcastReceiver中onReceive代碼也要盡量減少耗時,建議使用IntentService處理。

五、如何調查來解決ANR

1:首先分析log

2: 從trace.txt檔案檢視調用stack.

3: 看代碼

4:仔細檢視ANR的成因(iowait?block?memoryleak?)

通過ANR 日志定位問題

當ANR發生時,我們往往通過Logcat和traces檔案(目錄/data/anr/)的相關資訊輸出去定位問題。主要包含以下幾方面:

1)基本資訊,包括程序名、程序号、包名、系統build号、ANR 類型等等;

2)CPU使用資訊,包括活躍程序的CPU 平均占用率、IO情況等等;

3)線程堆棧資訊,所屬程序包括發生ANR的程序、其父程序、最近有活動的3個程序等等。

六、哪些地方是執行在主線程的

Activity的所有生命周期回調 都是執行在主線程的.

Service預設是執行在主線程的.

BroadcastReceiver的onReceive回調是執行在主線程的.

沒有使用子線程的looper的Handler的handleMessage, post(Runnable)是執行在主線程的.

AsyncTask的回調中除了doInBackground, 其他都是執行在主線程的.

View的post(Runnable)是執行在主線程的.

七、使用子線程的方式有哪些

7.1啟動Thread方式

class PrimeThread extends Thread {
        long minPrime;

        PrimeThread(long minPrime) {
            this.minPrime = minPrime;
        }

        public void run() {

        }
    }
//繼承Thread 
  PrimeThread p1 = new PrimeThread();
        p1.start();
           

class PrimeRun implements Runnable {
        long minPrime;

        PrimeRun(long minPrime) {
            this.minPrime = minPrime;
        }

        public void run() {
        }
    }
//實作Runnable接口
 PrimeRun p2 = new PrimeRun();
        new Thread(p2).start();
           

7.2使用AsyncTask

7.3IntentService

Service是運作在主線程的, 然而IntentService是運作在子線程的.

實際上IntentService就是實作了一個HandlerThread + ServiceHandler的模式.

7.4HandlerThread

Android中結合Handler和Thread的一種方式. 前面有雲, 預設情況下Handler的handleMessage是執行在主線程的, 但是如果我給這個Handler傳入了子線程的looper, handleMessage就會執行在這個子線程中的. HandlerThread正是這樣的一個結合體:

7.5Loader

Android 3.0引入的資料加載器, 可以在Activity/Fragment中使用. 支援異步加載資料, 并可監控資料源在資料發生變化時傳遞新結果. 常用的有CursorLoader, 用來加載資料庫資料.

特别注意:

使用Thread和HandlerThread時, 為了使效果更好, 建議設定Thread的優先級偏低一點:

Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);

因為如果沒有做任何優先級設定的話, 你建立的Thread預設和UI Thread是具有同樣的優先級的. 同樣的優先級的Thread, CPU排程上還是可能會阻塞掉你的UI Thread, 導緻ANR的.

結語

對于ANR問題, 個人認為還是預防為主, 認清代碼中的阻塞點, 善用線程. 同時形成良好的程式設計習慣, 要有MainThread和Worker Thread的概念。

導出traces檔案:

參考:

Android App優化之ANR詳解

如何分析ANR Log的總結

CPU饑餓問題

參考:饑餓問題

程式并行化以後,還會遇到共享資料的通路問題。

如果多個線程對共享資料都是隻讀操作,那麼對共享資料的通路不需要加鎖保護。

如果多個線程對共享資料的通路存在寫操作,那麼對共享資料的通路必須加鎖保護。

在有鎖保護的共享資料通路模型中,一旦一個線程取得了鎖,那麼其他線程在進行鎖操作時都必須等待。

這樣隻有一個線程在運作,導緻隻有一個CPU核在運作,其他CPU核都處于饑餓狀态。

解決共享資料通路的最有效方案就是共享資源分布式計算。

另外一種解決共享資料通路的方案是稱作”無鎖(Lock-Free)程式設計”,無鎖程式設計需要用到原子操作。

由于原子操作的速度比鎖快,是以在通路共享資源出現排隊情況下,其性能比使用鎖時得到提高;特别讀操作頻繁、寫操作稀少的情況,

無鎖程式設計也比有鎖程式設計有更高的性能,因為讀操作不需要鎖保護,另外無鎖程式設計還有一個優勢是它是非阻塞的算法。

當然,無鎖程式設計并不能解決通路共享資源出現的排隊問題,終極的解決方案仍然是共享資源分布式計算。

無鎖程式設計的另一缺陷是需要使用記憶體垃圾回收技術,對于不能使用垃圾收集機制的應用場合如伺服器軟體、嵌入式軟體來說是無法接受的。

最重要的一點,無鎖程式設計難度和複雜度非常高,非專業人士難以掌握,并且目前隻能通過它實作一些簡單的資料結構和算法。

雖然無鎖程式設計對于多核程式設計作用有限,但是它對于了解多線程程式設計的許多深層次問題還是有很好的借鑒作用。

無鎖程式設計還可以和共享資源分布式計算結合起來使用,以使程式性能獲得更大的提高,當然這需要硬體支援原子操作可以并行地操作不同的變量。

任務的分解與排程問題

程式并發運作後,除了鎖競争導緻的CPU饑餓問題外,還有一個問題也會導緻CPU饑餓,那就是任務的分解與排程問題。

所謂任務指的是執行的某個程式功能,任務與線程不同,一個線程内可以執行一個或多個任務。

如果任務劃分得不好,那麼就很難均勻地分布到各個CPU核上;

對于劃分好的多個任務,如何将其均勻地配置設定到各個CPU核上進行計算是很重要的問題,也就是所謂的任務排程問題。任務分解與排程的好壞會影響各CPU核上計算的負載均衡。

比如有6個任務,耗時分别為18、17、12、8、5、3,如何将其在一個雙核CPU上執行,取得好的加速比呢?

相信經過簡單的口算,大部分讀者都可以發現,将任務分為兩組(18、8、5),(17、12、3),使用兩個硬體線程來執行,每個線程執行其中的一組任務,能取得最好的加速比效果。

但是在實際情況中,任務數量較多,任務間耗時差距通常非常大,要均勻地将任務配置設定到各個CPU核上執行并非易事。事實上,任務的排程算法問題是一個NP難題。

當然上面的例子沒有考慮任務間的執行依賴關系,有依賴關系時,任務排程又複雜了一些。

在實際情況中,還有許多任務是動态産生的,事先并不知道任務耗時,如何排程這些動态任務,使計算均勻地配置設定到各個CPU核上則是更大的挑戰。

繼續閱讀