日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android App优化之ANR详解

發布時間:2023/12/20 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android App优化之ANR详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言

  • 背景:Android App優化, 要怎么做?
  • Android App優化之性能分析工具
  • Android App優化之提升你的App啟動速度之理論基礎
  • Android App優化之提升你的App啟動速度之實例挑戰
  • Android App優化之Layout怎么擺
  • Android App優化之ANR詳解
  • Android App優化之消除卡頓
  • Android App優化之內存優化
  • Android App優化之持久電量
  • Android App優化之如何高效網絡請求
  • App優化系列已近中期, 前面分享了一些工具,?理論, 也結合了案例談了下啟動優化,?布局分析等.

    原計劃將本文作為這個系列的一個承上啟下點, 對前面幾篇作一個小總結, 聊聊App流暢度和快速響應的話題.

    粗一縷, 發現內容還是很多, 暫且拆成幾篇來慢慢寫吧, 勿怪~

    今天先來聊聊ANR.

    1, 你碰到ANR了嗎

    在App使用過程中, 你可能遇到過這樣的情況:

    ANR

    恭喜你, 這就是傳說中的ANR.

    1.1 何為ANR

    ANR全名Application Not Responding, 也就是"應用無響應". 當操作在一段時間內系統無法處理時, 系統層面會彈出上圖那樣的ANR對話框.

    1.2 為什么會產生ANR

    在Android里, App的響應能力是由Activity Manager和Window Manager系統服務來監控的. 通常在如下兩種情況下會彈出ANR對話框:

    • 5s內無法響應用戶輸入事件(例如鍵盤輸入, 觸摸屏幕等).
    • BroadcastReceiver在10s內無法結束.

    造成以上兩種情況的首要原因就是在主線程(UI線程)里面做了太多的阻塞耗時操作, 例如文件讀寫, 數據庫讀寫, 網絡查詢等等.

    1.3 如何避免ANR

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

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

    這里面實際上涉及到兩個問題:

  • 哪些地方是運行在主線程的?
  • 不在主線程做, 在哪兒做?
  • 稍后解答.

    2, ANR分析

    2.1 獲取ANR產生的trace文件

    ANR產生時, 系統會生成一個traces.txt的文件放在/data/anr/下. 可以通過adb命令將其導出到本地:

    $adb pull data/anr/traces.txt .

    2.2 分析traces.txt

    2.2.1 普通阻塞導致的ANR

    獲取到的tracs.txt文件一般如下:

    如下以GithubApp代碼為例, 強行sleep thread產生的一個ANR.

    ----- pid 2976 at 2016-09-08 23:02:47 ----- Cmd line: com.anly.githubapp // 最新的ANR發生的進程(包名) ... DALVIK THREADS (41): "main" prio=5 tid=1 Sleeping | group="main" sCount=1 dsCount=0 obj=0x73467fa8 self=0x7fbf66c95000 | sysTid=2976 nice=0 cgrp=default sched=0/0 handle=0x7fbf6a8953e0 | state=S schedstat=( 0 0 0 ) utm=60 stm=37 core=1 HZ=100 | stack=0x7ffff4ffd000-0x7ffff4fff000 stackSize=8MB | held mutexes= at java.lang.Thread.sleep!(Native method) - sleeping on <0x35fc9e33> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:1031) - locked <0x35fc9e33> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:985) // 主線程中sleep過長時間, 阻塞導致無響應. at com.tencent.bugly.crashreport.crash.c.l(BUGLY:258) - locked <@addr=0x12dadc70> (a com.tencent.bugly.crashreport.crash.c) at com.tencent.bugly.crashreport.CrashReport.testANRCrash(BUGLY:166) // 產生ANR的那個函數調用 - locked <@addr=0x12d1e840> (a java.lang.Class<com.tencent.bugly.crashreport.CrashReport>) at com.anly.githubapp.common.wrapper.CrashHelper.testAnr(CrashHelper.java:23) at com.anly.githubapp.ui.module.main.MineFragment.onClick(MineFragment.java:80) // ANR的起點 at com.anly.githubapp.ui.module.main.MineFragment_ViewBinding$2.doClick(MineFragment_ViewBinding.java:47) at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22) at android.view.View.performClick(View.java:4780) at android.view.View$PerformClick.run(View.java:19866) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke!(Native method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

    拿到trace信息, 一切好說.
    如上trace信息中的添加的中文注釋已基本說明了trace文件該怎么分析:

  • 文件最上的即為最新產生的ANR的trace信息.
  • 前面兩行表明ANR發生的進程pid, 時間, 以及進程名字(包名).
  • 尋找我們的代碼點, 然后往前推, 看方法調用棧, 追溯到問題產生的根源.
  • 以上的ANR trace是屬于相對簡單, 還有可能你并沒有在主線程中做過于耗時的操作, 然而還是ANR了. 這就有可能是如下兩種情況了:

    2.2.2 CPU滿負荷

    這個時候你看到的trace信息可能會包含這樣的信息:

    Process:com.anly.githubapp ... CPU usage from 3330ms to 814ms ago: 6% 178/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20 major 4.6% 2976/com.anly.githubapp: 0.7% user + 3.7% kernel /faults: 52 minor 19 major 0.9% 252/com.android.systemui: 0.9% user + 0% kernel ... 100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait

    最后一句表明了:

  • 當是CPU占用100%, 滿負荷了.
  • 其中絕大數是被iowait即I/O操作占用了.
  • 此時分析方法調用棧, 一般來說會發現是方法中有頻繁的文件讀寫或是數據庫讀寫操作放在主線程來做了.

    2.2.3 內存原因

    其實內存原因有可能會導致ANR, 例如如果由于內存泄露, App可使用內存所剩無幾, 我們點擊按鈕啟動一個大圖片作為背景的activity, 就可能會產生ANR, 這時trace信息可能是這樣的:

    // 以下trace信息來自網絡, 用來做個示例 Cmdline: android.process.acoreDALVIK THREADS: "main"prio=5 tid=3 VMWAIT |group="main" sCount=1 dsCount=0 s=N obj=0x40026240self=0xbda8 | sysTid=1815 nice=0 sched=0/0 cgrp=unknownhandle=-1344001376 atdalvik.system.VMRuntime.trackExternalAllocation(NativeMethod) atandroid.graphics.Bitmap.nativeCreate(Native Method) atandroid.graphics.Bitmap.createBitmap(Bitmap.java:468) atandroid.view.View.buildDrawingCache(View.java:6324) atandroid.view.View.getDrawingCache(View.java:6178) ... MEMINFO in pid 1360 [android.process.acore] ** native dalvik other total size: 17036 23111 N/A 40147 allocated: 16484 20675 N/A 37159 free: 296 2436 N/A 2732

    可以看到free的內存已所剩無幾.

    當然這種情況可能更多的是會產生OOM的異常...

    2.2 ANR的處理

    針對三種不同的情況, 一般的處理情況如下

  • 主線程阻塞的
    開辟單獨的子線程來處理耗時阻塞事務.

  • CPU滿負荷, I/O阻塞的
    I/O阻塞一般來說就是文件讀寫或數據庫操作執行在主線程了, 也可以通過開辟子線程的方式異步執行.

  • 內存不夠用的
    增大VM內存, 使用largeHeap屬性, 排查內存泄露(這個在內存優化那篇細說吧)等.

  • 3, 深入一點

    沒有人愿意在出問題之后去解決問題.
    高手和新手的區別是, 高手知道怎么在一開始就避免問題的發生. 那么針對ANR這個問題, 我們需要做哪些層次的工作來避免其發生呢?

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

  • Activity的所有生命周期回調都是執行在主線程的.
  • Service默認是執行在主線程的.
  • BroadcastReceiver的onReceive回調是執行在主線程的.
  • 沒有使用子線程的looper的Handler的handleMessage, post(Runnable)是執行在主線程的.
  • AsyncTask的回調中除了doInBackground, 其他都是執行在主線程的.
  • View的post(Runnable)是執行在主線程的.
  • 3.2 使用子線程的方式有哪些

    上面我們幾乎一直在說, 避免ANR的方法就是在子線程中執行耗時阻塞操作. 那么在Android中有哪些方式可以讓我們實現這一點呢.

    3.2.1 啟Thread方式

    這個其實也是Java實現多線程的方式. 有兩種實現方法, 繼承Thread 或 實現Runnable接口:

    繼承Thread

    class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } } PrimeThread p = new PrimeThread(143); p.start();

    實現Runnable接口

    class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } } PrimeRun p = new PrimeRun(143); new Thread(p).start();

    3.2.2 使用AsyncTask

    這個是Android特有的方式, AsyncTask顧名思義, 就是異步任務的意思.

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { // 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"); } } // 啟動方式 new DownloadFilesTask().execute(url1, url2, url3);

    3.2.3 HandlerThread

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

    // 啟動一個名為new_thread的子線程 HandlerThread thread = new HandlerThread("new_thread"); thread.start();// 取new_thread賦值給ServiceHandler private ServiceHandler mServiceHandler; mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // 此時handleMessage是運行在new_thread這個子線程中了. } }

    3.2.4 IntentService

    Service是運行在主線程的, 然而IntentService是運行在子線程的.
    實際上IntentService就是實現了一個HandlerThread + ServiceHandler的模式.

    以上HandlerThread的使用代碼示例也就來自于IntentService源碼.

    3.2.5 Loader

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

    // Prepare the loader. Either re-connect with an existing one, // or start a new one. // 使用LoaderManager來初始化Loader getLoaderManager().initLoader(0, null, this); //如果 ID 指定的加載器已存在,則將重復使用上次創建的加載器。 //如果 ID 指定的加載器不存在,則 initLoader() 將觸發 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以實現代碼以實例化并返回新加載器 // 創建一個Loader public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } // 加載完成 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); }

    具體請參看官網Loader介紹.

    3.2.6 特別注意

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

    Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);

    因為如果沒有做任何優先級設置的話, 你創建的Thread默認和UI Thread是具有同樣的優先級的, 你懂的. 同樣的優先級的Thread, CPU調度上還是可能會阻塞掉你的UI Thread, 導致ANR的.

    結語

    對于ANR問題, 個人認為還是預防為主, 認清代碼中的阻塞點, 善用線程. 同時形成良好的編程習慣, 要有MainThread和Worker Thread的概念的...(實際上人的工作狀態也是這樣的~~哈哈)

    轉載于:https://www.cnblogs.com/xgjblog/p/9055109.html

    總結

    以上是生活随笔為你收集整理的Android App优化之ANR详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 1024在线视频| 天堂中文字幕在线观看 | 国产av一区二区不卡 | 天天做夜夜爱 | 91蜜桃网站| 欧美一区二区三区四区在线 | 国产精品五区 | 成人激情免费视频 | 性感美女在线观看 | 欧美特级一级片 | 中文字幕婷婷 | 91香蕉视频污污 | 亚洲av第一成肉网 | 欧美无极品| 琪琪五月天| 夜夜看av | 特级西西444www大精品视频免费看 | 综合色88| 草莓视频在线观看入口w | 三级福利片 | 欧美不卡一二三 | 成人影片在线播放 | 亚洲男人的天堂网站 | 男女吻胸做爰摸下身 | 丁香六月激情综合 | 性视频久久 | 97视频在线 | 一区二区三区在线免费观看 | 1024精品一区二区三区日韩 | 亚洲成人激情在线 | 夜夜精品视频 | 嫩草影院黄 | 精品久久久久中文慕人妻 | 欧美人与性动交α欧美片 | 亚洲国产一区二区在线观看 | 国产精品久久久久久久久久久久久久久久 | 日本xxxxwwwww| 91碰碰| 国产综合日韩 | 五月婷婷激情在线 | 伊人二区 | 日本高清免费不卡视频 | 亚洲综合色吧 | 国产精品第八页 | 亚洲天堂午夜 | 不卡视频在线观看 | 欧美日韩人妻精品一区 | 男人插入女人下面视频 | 青青草草视频 | 蜜臀av免费一区二区三区水牛 | 麻豆一区产品精品蜜桃的特点 | 98久久| 日本少妇喷水视频 | 秋霞自拍 | 亚洲天堂h| 天天操国产| 色一区二区 | 欧美性大战久久久久xxx | 欧洲av一区 | 少妇高潮一区二区三区69 | 中文在线а√在线8 | 亚洲成年人在线 | 可以在线观看的黄色 | 播播开心激情网 | 久久爱影视i | 人人操日日干 | 巨胸爆乳美女露双奶头挤奶 | 91一区二区三区在线观看 | 91精品国产综合久久久蜜臀粉嫩 | 国产原创视频在线 | 亚洲伦理在线视频 | 国产精品久久久久久一区二区三区 | 热久久精 | 成人啪啪18免费游戏链接 | a级黄色小说 | 国产视频中文字幕 | 亚洲国产精品毛片 | 极品新婚夜少妇真紧 | 色视频免费在线观看 | 最新中文字幕久久 | 善良的女邻居在线观看 | 日本黄色高清视频 | 69av导航 | 欧美成年人在线视频 | 中文字幕日本在线 | 午夜动态图 | 九九色九九 | 国产女大学生av | 精品视频免费播放 | 亚洲国产一二 | 少妇色综合 | 成人国产一区二区三区 | 欧美亚洲视频在线观看 | 金瓶狂野欧美性猛交xxxx | 日韩一中文字幕 | 男女做激情爱呻吟口述全过程 | 国产三级在线观看完整版 | 亚洲va在线观看 | 少女情窦初开的第4集在线观看 |