欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入學(xué)習(xí)Android?ANR?的原理分析及解決辦法

 更新時(shí)間:2021年11月24日 16:10:36   作者:巴菲貓  
Android系統(tǒng)中,AMS和WMS會檢測App的響應(yīng)時(shí)間,如果App在特定時(shí)間無法相應(yīng)屏幕觸摸或鍵盤輸入時(shí)間,或者特定事件沒有處理完畢,就會出現(xiàn)ANR。本文將帶領(lǐng)大學(xué)深入學(xué)習(xí)一下ANR的原理及解決辦法,感興趣的同學(xué)可以學(xué)習(xí)一下

一、ANR說明和原因

1.1 簡介

ANR全稱:Application Not Responding,也就是應(yīng)用程序無響應(yīng)。

1.2 原因

Android系統(tǒng)中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會檢測App的響應(yīng)時(shí)間,如果App在特定時(shí)間無法相應(yīng)屏幕觸摸或鍵盤輸入時(shí)間,或者特定事件沒有處理完畢,就會出現(xiàn)ANR。

以下四個(gè)條件都可以造成ANR發(fā)生:

  • InputDispatching Timeout:5秒內(nèi)無法響應(yīng)屏幕觸摸事件或鍵盤輸入事件
  • BroadcastQueue Timeout :在執(zhí)行前臺廣播(BroadcastReceiver)的onReceive()函數(shù)時(shí)10秒沒有處理完成,后臺為60秒。
  • Service Timeout :前臺服務(wù)20秒內(nèi),后臺服務(wù)在200秒內(nèi)沒有執(zhí)行完畢。
  • ContentProvider Timeout :ContentProvider的publish在10s內(nèi)沒進(jìn)行完。

1.3 避免

盡量避免在主線程(UI線程)中作耗時(shí)操作。

那么耗時(shí)操作就放在子線程中。

二、ANR分析辦法

2.1 ANR重現(xiàn)

這里使用的是號稱Google親兒子的Google Pixel xl(Android 8.0系統(tǒng))做的測試,生成一個(gè)按鈕跳轉(zhuǎn)到ANRTestActivity,在后者的onCreate()中主線程休眠20秒:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_anr_test);
    // 這是Android提供線程休眠函數(shù),與Thread.sleep()最大的區(qū)別是
    // 該使用該函數(shù)不會拋出InterruptedException異常。
    SystemClock.sleep(20 * 1000);
}

在進(jìn)入ANRTestActivity后黑屏一段時(shí)間,大概有七八秒,終于彈出了ANR異常。

2.2 ANR分析辦法一:Log

剛才產(chǎn)生ANR后,看下Log:

可以看到logcat清晰地記錄了ANR發(fā)生的時(shí)間,以及線程的tid和一句話概括原因:WaitingInMainSignalCatcherLoop,大概意思為主線程等待異常。

最后一句The application may be doing too much work on its main thread.告知可能在主線程做了太多的工作。

2.3 ANR分析辦法二:traces.txt

剛才的log有第二句Wrote stack traces to '/data/anr/traces.txt',說明ANR異常已經(jīng)輸出到traces.txt文件,使用adb命令把這個(gè)文件從手機(jī)里導(dǎo)出來:

1.cd到adb.exe所在的目錄,也就是Android SDK的platform-tools目錄,例如:

cd D:\Android\AndroidSdk\platform-tools

此外,除了Windows的cmd以外,還可以使用AndroidStudio的Terminal來輸入adb命令。

2.到指定目錄后執(zhí)行以下adb命令導(dǎo)出traces.txt文件:

adb pull /data/anr/traces.txt

traces.txt默認(rèn)會被導(dǎo)出到Android SDK的\platform-tools目錄。一般來說traces.txt文件記錄的東西會比較多,分析的時(shí)候需要有針對性地去找相關(guān)記錄。

----- pid 23346 at 2017-11-07 11:33:57 -----  ----> 進(jìn)程id和ANR產(chǎn)生時(shí)間
Cmd line: com.sky.myjavatest
Build fingerprint: 'google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keys'
ABI: 'arm64'
Build type: optimized
Zygote loaded classes=4681 post zygote classes=106
Intern table: 42675 strong; 137 weak
JNI: CheckJNI is on; globals=526 (plus 22 weak)
Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so 
/system/lib64/libjavacrypto.so
/system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libsoundpool.so
/system/lib64/libwebviewchromium_loader.so libjavacore.so libopenjdk.so (9)
Heap: 22% free, 1478KB/1896KB; 21881 objects    ----> 內(nèi)存使用情況

...

"main" prio=5 tid=1 Sleeping    ----> 原因?yàn)镾leeping
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x733d0670 self=0x74a4abea00
  | sysTid=23346 nice=-10 cgrp=default sched=0/0 handle=0x74a91ab9b0
  | state=S schedstat=( 391462128 82838177 354 ) utm=33 stm=4 core=3 HZ=100
  | stack=0x7fe6fac000-0x7fe6fae000 stackSize=8MB
  | held mutexes=
  at java.lang.Thread.sleep(Native method)
  - sleeping on <0x053fd2c2> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:373)
  - locked <0x053fd2c2> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:314)
  at android.os.SystemClock.sleep(SystemClock.java:122)
  at com.sky.myjavatest.ANRTestActivity.onCreate(ANRTestActivity.java:20) ----> 產(chǎn)生ANR的包名以及具體行數(shù)
  at android.app.Activity.performCreate(Activity.java:6975)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
  at android.app.ActivityThread.-wrap11(ActivityThread.java:-1)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
  at android.os.Handler.dispatchMessage(Handler.java:105)
  at android.os.Looper.loop(Looper.java:164)
  at android.app.ActivityThread.main(ActivityThread.java:6541)
  at java.lang.reflect.Method.invoke(Native method)
  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

在文件中使用 ctrl + F 查找包名可以快速定位相關(guān)代碼。

  • 通過上方log可以看出相關(guān)問題:
  • 進(jìn)程id和包名:pid 23346 com.sky.myjavatest
  • 造成ANR的原因:Sleeping
  • 造成ANR的具體行數(shù):ANRTestActivity.java:20類的第20行

特別注意:產(chǎn)生新的ANR,原來的 traces.txt 文件會被覆蓋。

2.4 ANR分析辦法三:Java線程調(diào)用分析

通過JDK提供的命令可以幫助分析和調(diào)試Java應(yīng)用,命令為:

jstack {pid}

其中pid可以通過jps命令獲得,jps命令會列出當(dāng)前系統(tǒng)中運(yùn)行的所有Java虛擬機(jī)進(jìn)程,比如

7266 Test
7267 Jps

2.5 ANR分析辦法四:DDMS分析ANR問題

  • 使用DDMS——Update Threads工具
  • 閱讀Update Threads的輸出

三、造成ANR的原因及解決辦法

上面例子只是由于簡單的主線程耗時(shí)操作造成的ANR,造成ANR的原因還有很多:

主線程阻塞或主線程數(shù)據(jù)讀取

解決辦法:避免死鎖的出現(xiàn),使用子線程來處理耗時(shí)操作或阻塞任務(wù)。盡量避免在主線程query provider、不要濫用SharePreferenceS

CPU滿負(fù)荷,I/O阻塞

解決辦法:文件讀寫或數(shù)據(jù)庫操作放在子線程異步操作。

內(nèi)存不足

解決辦法:AndroidManifest.xml文件<applicatiion>中可以設(shè)置 android:largeHeap="true",以此增大App使用內(nèi)存。不過不建議使用此法,從根本上防止內(nèi)存泄漏,優(yōu)化內(nèi)存使用才是正道。

各大組件ANR

各大組件生命周期中也應(yīng)避免耗時(shí)操作,注意BroadcastReciever的onRecieve()、后臺Service和ContentProvider也不要執(zhí)行太長時(shí)間的任務(wù)。

四、ANR源碼分析

4.1 Service造成的Service Timeout

Service Timeout是位于"ActivityManager"線程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息時(shí)觸發(fā)。

4.1.1 發(fā)送延時(shí)消息

Service進(jìn)程attach到system_server進(jìn)程的過程中會調(diào)用realStartServiceLocked,緊接著mAm.mHandler.sendMessageAtTime()來發(fā)送一個(gè)延時(shí)消息,延時(shí)的時(shí)常是定義好的,如前臺Service的20秒。ActivityManager線程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息時(shí)會觸發(fā)。

AS.realStartServiceLocked

ActiveServices.java

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    ...
    //發(fā)送delay消息(SERVICE_TIMEOUT_MSG)
    bumpServiceExecutingLocked(r, execInFg, "create");
    try {
        ...
        //最終執(zhí)行服務(wù)的onCreate()方法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        ...
    }
}

AS.bumpServiceExecutingLocked

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    ... 
    scheduleServiceTimeoutLocked(r.app);
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    long now = SystemClock.uptimeMillis();
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;

    //當(dāng)超時(shí)后仍沒有remove該SERVICE_TIMEOUT_MSG消息,則執(zhí)行service Timeout流程
    mAm.mHandler.sendMessageAtTime(msg,
        proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}

4.1.2 進(jìn)入目標(biāo)進(jìn)程的主線程創(chuàng)建Service

經(jīng)過Binder等層層調(diào)用進(jìn)入目標(biāo)進(jìn)程的主線程 handleCreateService(CreateServiceData data)。

ActivityThread.java

   private void handleCreateService(CreateServiceData data) {
        ...
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        Service service = (Service) cl.loadClass(data.info.name).newInstance();
        ...

        try {
            //創(chuàng)建ContextImpl對象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            //創(chuàng)建Application對象
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //調(diào)用服務(wù)onCreate()方法 
            service.onCreate();

            //取消AMS.MainHandler的延時(shí)消息
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (Exception e) {
            ...
        }
    }

這個(gè)方法中會創(chuàng)建目標(biāo)服務(wù)對象,以及回調(diào)常用的Service的onCreate()方法,緊接著通過serviceDoneExecuting()回到system_server執(zhí)行取消AMS.MainHandler的延時(shí)消息。

4.1.3 回到system_server執(zhí)行取消AMS.MainHandler的延時(shí)消息

AS.serviceDoneExecutingLocked

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing) {
    ...
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                //當(dāng)前服務(wù)所在進(jìn)程中沒有正在執(zhí)行的service
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        ...
    }
    ...
}

此方法中Service邏輯處理完成則移除之前延時(shí)的消息SERVICE_TIMEOUT_MSG。如果沒有執(zhí)行完畢不調(diào)用這個(gè)方法,則超時(shí)后會發(fā)出SERVICE_TIMEOUT_MSG來告知ANR發(fā)生。

4.2 BroadcastReceiver造成的BroadcastQueue Timeout

BroadcastReceiver Timeout是位于"ActivityManager"線程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息時(shí)觸發(fā)。

4.2.1 處理廣播函數(shù) processNextBroadcast() 中 broadcastTimeoutLocked(false) 發(fā)送延時(shí)消息

廣播處理順序?yàn)橄忍幚聿⑿袕V播,再處理當(dāng)前有序廣播。

final void processNextBroadcast(boolean fromMsg) {
    synchronized(mService) {
        ...
        // 處理當(dāng)前有序廣播
        do {
            r = mOrderedBroadcasts.get(0);
            //獲取所有該廣播所有的接收者
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                    //step 1\. 發(fā)送延時(shí)消息,這個(gè)函數(shù)處理了很多事情,比如廣播處理超時(shí)結(jié)束廣播
                    broadcastTimeoutLocked(false);
                    ...
                }
            }
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    //2\. 處理廣播消息消息
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                }
                //3\. 取消廣播超時(shí)ANR消息
                cancelBroadcastTimeoutLocked();
            }
        } while (r == null);
        ...

        // 獲取下條有序廣播
        r.receiverTime = SystemClock.uptimeMillis();
        if (!mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            //設(shè)置廣播超時(shí)
            setBroadcastTimeoutLocked(timeoutTime);
        }
        ...
    }
}

上文的step 1. broadcastTimeoutLocked(false)函數(shù):記錄時(shí)間信息并調(diào)用函數(shù)設(shè)置發(fā)送延時(shí)消息

final void broadcastTimeoutLocked(boolean fromMsg) {
    ...
        long now = SystemClock.uptimeMillis();
        if (fromMsg) {
            if (mService.mDidDexOpt) {
                // Delay timeouts until dexopt finishes.
                mService.mDidDexOpt = false;
                long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
            if (!mService.mProcessesReady) {
                return;
            }

            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            if (timeoutTime > now) {
                // step 2
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }

上文的step 2.setBroadcastTimeoutLocked函數(shù): 設(shè)置廣播超時(shí)具體操作,同樣是發(fā)送延時(shí)消息

final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

4.2.2 setBroadcastTimeoutLocked(long timeoutTime)函數(shù)的參數(shù)timeoutTime是當(dāng)前時(shí)間加上設(shè)定好的超時(shí)時(shí)間。

也就是上文的

long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;

mTimeoutPeriod 也就是前臺隊(duì)列的10s和后臺隊(duì)列的60s。

public ActivityManagerService(Context systemContext) {
    ...
    static final int BROADCAST_FG_TIMEOUT = 10 * 1000;
    static final int BROADCAST_BG_TIMEOUT = 60 * 1000;
    ...
    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "foreground", BROADCAST_FG_TIMEOUT, false);
    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "background", BROADCAST_BG_TIMEOUT, true);
    ...
}

4.2.3 在processNextBroadcast()過程,執(zhí)行完performReceiveLocked后調(diào)用cancelBroadcastTimeoutLocked

cancelBroadcastTimeoutLocked :處理廣播消息函數(shù) processNextBroadcast() 中 performReceiveLocked() 處理廣播消息完畢則調(diào)用 cancelBroadcastTimeoutLocked() 取消超時(shí)消息。

final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
        mPendingBroadcastTimeoutMessage = false;
    }
}

4.3 ContentProvider的ContentProvider Timeout

ContentProvider Timeout是位于”ActivityManager”線程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息時(shí)觸發(fā)。

五、Android ANR的信息收集

無論是四大組件或者進(jìn)程等只要發(fā)生ANR,最終都會調(diào)用AMS.appNotResponding()方法。

參考:理解Android ANR的信息收集過程

以上就是深入學(xué)習(xí)Android ANR 的原理分析及解決辦法的詳細(xì)內(nèi)容,更多關(guān)于Android ANR的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android倒計(jì)時(shí)功能的實(shí)現(xiàn)代碼

    Android倒計(jì)時(shí)功能的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Android倒計(jì)時(shí)功能的實(shí)現(xiàn)代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • android studio 的下拉菜單Spinner使用詳解

    android studio 的下拉菜單Spinner使用詳解

    這篇文章主要介紹了android studio 的下拉菜單Spinner使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Android解析XML(PULL)展示到ListView

    Android解析XML(PULL)展示到ListView

    這篇文章主要為大家詳細(xì)介紹了Android解析XML(PULL)展示到ListView的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Android實(shí)現(xiàn)梯形TextView效果

    Android實(shí)現(xiàn)梯形TextView效果

    TextView(文本框),用于顯示文本的一個(gè)控件,Android開發(fā)中經(jīng)常使用,本文講述如何實(shí)現(xiàn)一個(gè)梯形的TextView
    2021-05-05
  • 一些有效的Android啟動優(yōu)化策略分享

    一些有效的Android啟動優(yōu)化策略分享

    在當(dāng)今激烈競爭的移動應(yīng)用市場,應(yīng)用的啟動速度直接影響著用戶的第一印象和滿意度,Android的啟動優(yōu)化是開發(fā)者必須關(guān)注的關(guān)鍵領(lǐng)域,本文將詳細(xì)介紹一些強(qiáng)大有效的Android啟動優(yōu)化策略,幫助你優(yōu)化應(yīng)用的啟動過程,為用戶創(chuàng)造更出色的體驗(yàn),需要的朋友可以參考下
    2023-08-08
  • 詳解Android運(yùn)行時(shí)權(quán)限及APP適配方法

    詳解Android運(yùn)行時(shí)權(quán)限及APP適配方法

    本篇文章給大家詳細(xì)分析了Android運(yùn)行時(shí)權(quán)限及APP適配方法,并把重要知識點(diǎn)做了說明,有需要的朋友參考下。
    2018-03-03
  • Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的代碼詳解

    Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的代碼詳解

    小編經(jīng)常遇到這樣的需求類似進(jìn)入開發(fā)者模式,即多次點(diǎn)擊后執(zhí)行操作。下面小編通過實(shí)例代碼給大家講解Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的相關(guān)知識,感興趣的朋友跟隨小編一起學(xué)習(xí)吧
    2018-10-10
  • Android動態(tài)使用VectorDrawable過程詳解

    Android動態(tài)使用VectorDrawable過程詳解

    這篇文章主要介紹了Android動態(tài)使用VectorDrawable過程,2014年6月26日的I/O 2014開發(fā)者大會上谷歌正式推出了Android L,它帶來了全新的設(shè)計(jì)語言Material Design,新的API也提供了這個(gè)類VectorDrawable
    2023-02-02
  • Ubutu1604安裝colmap實(shí)現(xiàn)方法詳細(xì)教程

    Ubutu1604安裝colmap實(shí)現(xiàn)方法詳細(xì)教程

    這篇文章主要介紹了Ubutu1604安裝colmap實(shí)現(xiàn)方法詳細(xì)教程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Android  ImageView繪制圓角效果

    Android ImageView繪制圓角效果

    這篇文章主要介紹了Android ImageView繪制圓角效果,一種是使用Xfermode,另一種是BitmapShader來實(shí)現(xiàn)圓形和圓角的效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01

最新評論