Android進階知識:ANR的定位與解決

1、前言

ANR對于Android開發者來說一定不會陌生,從剛開始學習Android時的一不注意就ANR,到后來知道主線程不能進行耗時操作注意到這點后,程序出現ANR的情況就大大減少了,甚至于消失了。那么真的是只要在主線程做耗時操作就會產生ANR嗎?為什么在有時候明明覺得自己沒在主線程做耗時操作也出現了ANR呢?一旦出現莫名其妙的ANR,怎么定位導致ANR的產生的位置和解決問題呢?那么接下來就來一個個的解決這些問題。

2、ANR是什么?

ANR全稱Application Not Responding即應用程序無響應。在Android中如果應用程序有一段時間無法響應用戶操作,系統會彈出彈窗,讓用戶選擇是繼續等待還是強制關閉程序。一款良好應用APP是不應該出現這個彈窗的。

3、ANR的產生原因

ANR產生原因和類型有以下幾種:

1、Activity在5秒鐘之內無法響應屏幕觸摸事件揮著鍵盤輸入事件就會產生ANR

KeyDispatchTimeout
Reason:Input event dispatching timed out

2、BroadcastReceiver在10秒鐘之內還未執行完成就會產生ANR

BroadcastTimeout
Reason:Timeout of broadcast BroadcastRecord

3、Service各個生命周期在20秒鐘之內沒有執行完成就會產生ANR

ServiceTimeout
Reason:Timeout executing service

4、ContentProvider在10秒鐘之內沒有執行完成就會產生ANR

ContentProviderTimeout
Reason:timeout publishing content providers

在以上這幾種原因中出現最多的一般是第一種,而且往往都是因為在寫代碼時不注意,在主線程做了耗時的操作。

4、ANR的定位與解決

關于ANR的定位這里舉一個例子來看。這是我之前遇到的一次出現ANR的時候所解決問題的情況和解決步驟。

?

首先當然是復現ANR現象,找準ANR出現的地方,查看對應代碼,如果能直接看出來問題所在,找到代碼中做的錯誤操作那么直接修改相應代碼就解決問題了。但是如果沒法輕易看出問題原因,接下來就只好去Logcat中查看對應的錯誤日志。

07-22 21:39:17.019 819-851/? E/ActivityManager: ANR in com.xxxx.performance (com.xxxx.performance/.view.home.activity.MainActivity)PID: 7398Reason: Input dispatching timed out (com.xxxx.performance/com.xxxx.performance.view.home.activity.MainActivity, Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 29.  Wait queue head age: 8579.3ms.)Load: 18.22 / 18.1 / 18.18CPU usage from 0ms to 8653ms later:124% 7398/com.xxxx.performance: 118% user + 6.5% kernel / faults: 4962 minor 7 major82% 819/system_server: 28% user + 53% kernel / faults: 10555 minor 11 major23% 4402/adbd: 1% user + 22% kernel10% 996/com.android.systemui: 4.6% user + 6.2% kernel / faults: 4677 minor 1 major4.6% 2215/com.android.phone: 1.5% user + 3.1% kernel / faults: 5411 minor6.3% 6268/perfd: 3.4% user + 2.8% kernel / faults: 134 minor0.5% 1149/com.miui.whetstone: 0.1% user + 0.3% kernel / faults: 3016 minor 1 major0.2% 2097/com.xiaomi.finddevice: 0.1% user + 0.1% kernel / faults: 2256 minor0.6% 2143/com.miui.daemon: 0.2% user + 0.3% kernel / faults: 2798 minor1.2% 1076/com.xiaomi.xmsf: 0.6% user + 0.6% kernel / faults: 2802 minor0% 2122/com.android.server.telecom: 0% user + 0% kernel / faults: 2929 minor0% 2244/com.miui.contentcatcher: 0% user + 0% kernel / faults: 1800 minor0% 2267/com.mediatek.nlpservice: 0% user + 0% kernel / faults: 2052 minor0% 2166/com.xiaomi.mitunes: 0% user + 0% kernel / faults: 1797 minor 3 major0% 2190/com.fingerprints.service: 0% user + 0% kernel / faults: 1857 minor0.1% 154/mmcqd/0: 0% user + 0.1% kernel0.4% 8069/logcat: 0.3% user + 0.1% kernel......

從日志第一行開始看,可以看到發生錯誤的應用包名和類名,這里是ANR in com.xxxx.performance (com.xxxx.performance/.view.home.activity.MainActivity)。接著看到進程號PID7398。發生ANRReasonInput dispatching timed out就是上面提到的第一種。再往下就是活躍進程的CPU占用率日志。

   124% 7398/com.xxxx.performance82% 819/system_server10% 996/com.android.systemui4.6% 2215/com.android.phone......

光看Logcat中的日志只能看到這些信息,大概知道是在MainActivity出現了問題,但還是不能清楚的定位到發生ANR的代碼行,想要獲得進一步的錯誤信息只能通過查看ANR過程中生成的堆棧信息文件traces.txt了。

traces.txt文件位置在/data/anr/目錄下,可以通過以下adb命令將其拷貝到sd卡目錄下獲取查看。

adb shell
cat  /data/anr/traces.txt  >/mnt/sdcard/traces.txt  
exit

traces.txt里的信息:

DALVIK THREADS (42):
"main" prio=5 tid=1 Native| group="main" sCount=1 dsCount=0 obj=0x75ceafb8 self=0x55933ae7e0| sysTid=7398 nice=0 cgrp=default sched=0/0 handle=0x7f7ddae0f0| state=S schedstat=( 101485399944 3411372871 31344 ) utm=9936 stm=212 core=1 HZ=100| stack=0x7fc8d40000-0x7fc8d42000 stackSize=8MB| held mutexes=kernel: __switch_to+0x74/0x8ckernel: futex_wait_queue_me+0xcc/0x158kernel: futex_wait+0x120/0x20ckernel: do_futex+0x184/0xa48kernel: SyS_futex+0x88/0x19ckernel: cpu_switch_to+0x48/0x4cnative: #00 pc 00017750  /system/lib64/libc.so (syscall+28)native: #01 pc 000d1584  /system/lib64/libart.so (_ZN3art17ConditionVariable4WaitEPNS_6ThreadE+140)native: #02 pc 00388098  /system/lib64/libart.so (_ZN3artL12GoToRunnableEPNS_6ThreadE+1068)native: #03 pc 000a5db8  /system/lib64/libart.so (_ZN3art12JniMethodEndEjPNS_6ThreadE+24)native: #04 pc 000280e4  /data/dalvik-cache/arm64/system@framework@boot.oat (Java_android_graphics_Paint_native_1init__+156)at android.graphics.Paint.native_init(Native method)at android.graphics.Paint.<init>(Paint.java:435)at android.graphics.Paint.<init>(Paint.java:425)at android.text.TextPaint.<init>(TextPaint.java:49)at android.text.Layout.<init>(Layout.java:160)at android.text.StaticLayout.<init>(StaticLayout.java:111)at android.text.StaticLayout.<init>(StaticLayout.java:87)at android.text.StaticLayout.<init>(StaticLayout.java:66)at android.widget.TextView.makeSingleLayout(TextView.java:6543)at android.widget.TextView.makeNewLayout(TextView.java:6383)at android.widget.TextView.checkForRelayout(TextView.java:7096)at android.widget.TextView.setText(TextView.java:4082)at android.widget.TextView.setText(TextView.java:3940)at android.widget.TextView.setText(TextView.java:3915)at com.xxxx.performance.view.home.fragment.AttendanceCheckInFragment.onNowTimeSuccess(AttendanceCheckInFragment.java:887)at com.xxxx.performance.presenter.attendance.AttendanceFragmentPresenter$6.onNext(AttendanceFragmentPresenter.java:214)at com.xxxx.performance.presenter.attendance.AttendanceFragmentPresenter$6.onNext(AttendanceFragmentPresenter.java:205)at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:198)at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:250)at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)............

還是從頭開始看,來看每個字段對應的含義:
線程名:main
線程優先級:prio=5
線程鎖ID: tid=1
線程狀態:Native 線程組名稱:group="main"
線程被掛起的次數:sCount=1
線程被調試器掛起的次數:dsCount=0
線程的java的對象地址:obj=0x75ceafb8
線程本身的Native對象地址:self=0x55933ae7e0
線程調度信息:
Linux系統中內核線程ID: sysTid=7398與主線程的進程號相同
線程調度優先級:nice=0
線程調度組:cgrp=default
線程調度策略和優先級:sched=0/0
線程處理函數地址:handle=0x7f7ddae0f0
線程的上下文信息:
線程調度狀態:state=S
線程在CPU中的執行時間、線程等待時間、線程執行的時間片長度:schedstat=(101485399944 3411372871 31344 )
線程在用戶態中的調度時間值:utm=9936
線程在內核態中的調度時間值:stm=212
最后執行這個線程的CPU核序號:core=1
線程的堆棧信息:
堆棧地址和大小:stack=0x7fc8d40000-0x7fc8d42000 stackSize=8MB
最后看到堆棧信息里的這一行:

at com.xxxx.performance.view.home.fragment.AttendanceCheckInFragment.onNowTimeSuccess(AttendanceCheckInFragment.java:887)

這里就看清楚了是在AttendanceCheckInFragment中的887行出現的問題,再到對應代碼行中就很容易發現ANR的原因了。

5、ANR的相關問題

  • 在Activity的onCreate方法里調用sleep方法會發生ANR嗎?

以前一直認為在主線程做了耗時操作就會發生ANR,那么真的是這樣嗎?在ActivityonCreate方法里調用Thread.sleep(60 * 1000)讓主線程sleep60秒,會導致應用程序ANR嗎?寫個Demo測試一下。

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);try {Log.d("ANR","開始sleep");Thread.sleep(60*1000);Log.d("ANR","sleep完成");} catch (InterruptedException e) {e.printStackTrace();}}
}

如上代碼,運行程序,結果應用沒有發生ANR,在sleep了60秒后正常打印日志。

?

再次運行程序,這回在程序運行后按下返回鍵查看現象:

?

這次果然就ANR了。通過這個例子,顯而易見的得到了這個問題的正確答案。在ActivityonCreate方法里調用sleep方法或者說做耗時操作,不一定會產生ANR。其實從ANR本身意為應用程序沒有響應,同時根據上面總結的ANR原因就可以看出,耗時操作本身是不會產生ANR的,導致ANR的根本還是應用程序無法在一定時間內響應用戶的操作。所以因為主線程被耗時操作占用了,主線成程無法對下一個操作進行響應才會ANR,沒有需要響應的操作自然就不會產生ANR,或者應該這樣說:主線程做耗時操作,非常容易引發ANR

6、總結

  • 光在主線程做耗時操作不會產生ANR,超時響應用戶操作才會產生ANR。
  • ANR的定位方法主要是根據Logcat中日志和ANR過程中生成的堆棧信息文件traces.txt。
  • 解決問題不如預防問題,寫代碼的時候要注意預防產生ANR。
  • 預防ANR的產生不光是在Activity中注意要把耗時操作放到子線程中去,還要注意在使用其他三個組件時,在其生命周期中同樣不能做太耗時的操作。另外在使用多線程時候要注意同步和死鎖的情況,一旦產生死鎖主線程同樣會引發ANR。

作者:胖宅老鼠
鏈接:https://juejin.cn/post/6844904069731975176
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/162064.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/162064.shtml
英文地址,請注明出處:http://en.pswp.cn/news/162064.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RabbitMQ 的網頁界面操作說明

啟動 上面給用戶添加了角色和權限&#xff0c; 我們就可以登錄了 先手動創建兩個隊列&#xff0c;然后再把這兩個隊列和交換機綁定&#xff0c;就可以發布消息 回到隊列中看看有什么變化 隊列中顯示綁定了交換機 再看一下隊列中發生的變化 可以看到隊列中收到了信息

2023亞太杯數學建模B題:玻璃溫室中的微氣候法規,思路模型代碼論文

問題B 玻璃溫室中的微氣候法規 賽題思路&#xff1a;思路獲取見文末名片&#xff0c;第一時間更新 溫室作物的產量受到各種氣候因素的影響&#xff0c;包括溫度、濕度和風速[1]。其中&#xff0c;適 宜的溫度和風速是植物生長[2]的關鍵。為了調節玻璃溫室內的溫度、風速等氣…

基于C#實現樹狀數組

有一種數據結構是神奇的&#xff0c;神秘的&#xff0c;它展現了位運算與數組結合的神奇魅力&#xff0c;太牛逼的&#xff0c;它就是樹狀數組&#xff0c;這種數據結構不是神人是發現不了的。 一、概序 假如我現在有個需求&#xff0c;就是要頻繁的求數組的前 n 項和&#x…

Day29| Leetcode 491. 遞增子序列 Leetcode 46. 全排列 Leetcode 47. 全排列 II

Leetcode 491. 遞增子序列 題目鏈接 491 遞增子序列 本題目和前面的樹層去重不太一樣&#xff0c;因為這里不能對原集合進行排序&#xff0c;所以之前的去重就沒法使用了&#xff0c;只能單純的記錄該元素有沒有使用過&#xff08;去重&#xff09;&#xff0c;下面使用的是…

Redis事務的理解與使用

文章目錄 Redis 事務1)基本認識2)事務操作1.MULTI2.EXEC3.錯誤處理4.DISCARD5.WATCH6.SCRIPT Redis 事務 官方文檔&#xff0c;永遠是你學習的第一手資料&#xff1a;Redis 事務 1)基本認識 談到事務&#xff0c;大家首先都會聯想到 mysql 中復雜但又功能強大的“事務”&…

MySQL面試題總結

1. 表之間如何關聯 表與表之間常用的關聯方式有兩種&#xff1a;內連接、外連接&#xff0c;下面以MySQL為例來說明這兩種連接方式 內連接通過INNER JOIN來實現&#xff0c;它將返回兩張表中滿足連接條件的數據&#xff0c;不滿足條件的數據不會查詢出來 外連接 外連接通過OUTE…

SpringBoot——感謝尚硅谷官方文檔

SpringBoot——感謝尚硅谷官方文檔 1 Spring與SpringBoot1、Spring能做什么1.1、Spring的能力1.2、Spring的生態1.3、Spring5重大升級1.3.1、響應式編程1.3.2、內部源碼設計 2、為什么用SpringBoot2.1、SpringBoot優點2.2、SpringBoot缺點 3、時代背景3.1、微服務3.2、分布式分…

Dubbo從入門到上天系列第十八篇:Dubbo引入Zookeeper等注冊中心簡介以及DubboAdmin簡要介紹,為后續詳解Dubbo各種注冊中心做鋪墊!

文章目錄 一&#xff1a;Dubbo注冊中心引言 1&#xff1a;什么是Dubbo的注冊中心&#xff1f; 2&#xff1a;注冊中心關系圖解 3&#xff1a;引入注冊中心服務執行流程 4&#xff1a;Dubbo注冊中心好處 5&#xff1a;注冊中心核心作用 二&#xff1a;注冊中心實現方案 …

10.docker的網絡network-概述

1.docker的網絡模式 docker共有四種網路模式&#xff0c;分別是bridge、host、none和container. 1.1 bridge bridge,也稱為虛擬網橋。在bridge模式下&#xff0c;為每個容器分配、配置IP等&#xff0c;并將容器連接到一個docker0。使用–network bridge命令指定&#xff0c;…

【01】ES6:ECMAScript 介紹

ECMAScript 6.0 &#xff08;以下簡稱 ES6&#xff09;是 JavaScript 語言的下一代標準&#xff0c;已經在 2015 年 6 月正式發布。它的目標&#xff0c;是使得 JavaScript 語言可以用來編寫復雜的大型應用程序&#xff0c;成為企業級開發語言。 參考&#xff1a;《ECMAScript…

C# Winform使用log4net記錄日志

寫在前面 Log4Net是從Java的log4j移植過來的&#xff0c;功能也與log4j類似&#xff0c;可以把日志信息輸出到文件、數據庫、控制臺、Windows 事件日志、遠程系統日志服務等不同的介質或目標。 Log4Net配置選項豐富靈活&#xff0c;并且可在運行時動態更新配置并應用&#xf…

解析紫光展銳T820 5G芯片——讓照片接近原色

紫光展銳系統級安全的高性能5G SoC芯片平臺T820&#xff0c;采用八核CPU架構&#xff0c;6nm EUV先進工藝&#xff0c;金融級全內置安全方案&#xff0c;在性能、功耗與5G通信體驗等方面&#xff0c;較上一代產品更為出色。 此前&#xff0c;已經為大家講解過T820的拍照、安全性…

mac跑分工具 Geekbench v6.2.2

Geekbench 6 是一款跨平臺的系統性能測試軟件&#xff0c;可以對處理器和內存等硬件進行評測&#xff0c;并提供了單核和多核兩種測試模式。該軟件適用于 Windows、macOS、Linux 和 iOS 等多種操作系統平臺。 Geekbench 6 測試可以幫助用戶快速準確地了解自己設備的性能表現&am…

【brpc學習實踐五】brpc自適應限流案例

自適應限流 服務的處理能力是有客觀上限的。當請求速度超過服務的處理速度時&#xff0c;服務就會過載。 如果服務持續過載&#xff0c;會導致越來越多的請求積壓&#xff0c;最終所有的請求都必須等待較長時間才能被處理&#xff0c;從而使整個服務處于癱瘓狀態。 與之相對…

PHP中間件實現

目錄 1、簡單中間實現 2、使用閉包函數實現中間件 在PHP中&#xff0c;中間件是一種常用的設計模式&#xff0c;用于處理請求和響應&#xff0c;它可以在請求到達目標處理程序之前或響應發送給客戶端之前執行一些特定的邏輯。中間件提供了一種靈活的方式來修改或擴展應用程序的…

查看當前laravel版本三種方法(筆記二)

1、在終端中使用 Artisan 命令&#xff1a;在 Laravel 項目的根目錄下&#xff0c;打開終端&#xff08;命令行界面&#xff09;&#xff0c;然后運行以下命令&#xff1a; php artisan --version 2、控制器中打印版本 var_dump(app()->version()); 3、在 Laravel 項目的根目…

【kubernetes】k8s架構之節點

文章目錄 1、集群架構示意圖2、概述3、管理3.1 節點名稱唯一性3.2 節點自注冊3.3 手動節點管理 4、節點狀態4.1 地址&#xff08;Addresses&#xff09;4.2 狀況&#xff08;Condition&#xff09;4.3 容量&#xff08;Capacity&#xff09;與可分配&#xff08;Allocatable&am…

PTA-輸出三角形面積和周長

本題要求編寫程序&#xff0c;根據輸入的三角形的三條邊a、b、c&#xff0c;計算并輸出面積和周長。注意&#xff1a;在一個三角形中&#xff0c; 任意兩邊之和大于第三邊。三角形面積計算公式&#xff1a;areas(s?a)(s?b)(s?c)?&#xff0c;其中s(abc)/2。 輸入格式&…

某60區塊鏈安全之Call函數簇濫用實戰二學習記錄

區塊鏈安全 文章目錄 區塊鏈安全Call函數簇濫用實戰二實驗目的實驗環境實驗原理實驗內容實驗步驟EXP利用 Call函數簇濫用實戰二 實驗目的 學會使用python3的web3模塊 學會并區分以太坊call、staticcall、delegatecall三種函數調用的特點 找到合約漏洞進行分析并形成利用 實驗…

關于git hooks

Git hooks 是一種在 Git 倉庫中觸發自定義腳本的機制。這些腳本可以在特定的 Git 操作&#xff08;如提交、推送、合并等&#xff09;發生時執行。通過使用 Git hooks&#xff0c;你可以在版本控制的不同階段自動運行腳本&#xff0c;以執行一些定制化的操作。 在 Git 中&…