Android 如何分析解决Android ANR

一 、什么是ANR

ANR: Application Not Responding,即应用无响应

二、ANR的类型

ANR一般有三种类型:

1:KeyDispatchTimeout(5 seconds) --主要类型

按键或触摸事件在特定时间内无响应

2:BroadcastTimeout(10 seconds)

BroadcastReceiver在特定时间内无法处理完成

3:ServiceTimeout(20 seconds) --小概率类型

Service在特定的时间内无法处理完成

三、KeyDispatchTimeout

Akey or touch event was not dispatched within the specified time(按键或触摸事件在特定时间内无响应)

具体的超时时间的定义在framework下的

ActivityManagerService.java

//How long we wait until we timeout on key dispatching.

staticfinal int KEY_DISPATCHING_TIMEOUT = 5*1000

四、为什么会超时呢?

超时时间的计数一般是从按键分发给app开始。超时的原因一般有两种:

(1)当前的事件没有机会得到处理(即UI线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)

(2)当前的事件正在处理,但没有及时完成

五、如何避免KeyDispatchTimeout

1:UI线程尽量只做跟UI相关的工作

2:耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理

3:尽量用Handler来处理UIthread和别的thread之间的交互

六、UI线程

说了那么多的UI线程,那么哪些属于UI线程呢?

UI线程主要包括如下:

1.Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc

2.AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc

3.Mainthread handler: handleMessage(), post*(runnable r), etc

4.other

七:如何去分析ANR 总结观察log文件的基本步骤

1.从log中搜索“ANR in”或“am_anr”,会找到ANR发生的log,该行会包含了ANR的时间、进程、是何种ANR等信息

2.如果是ForceClosed 和其它异常退出信息,则搜索"Fatal" 关键词, 快速定位到关键事件信息 。

3.定位到关键事件信息后 , 如果信息不够明确的,再去搜索应用程序包的虚拟机信息 ,查看具体的进程和线程跟踪的日志,来定位到代码 。

例:打开log文件 , 由于是ANR错误,因此搜索"ANR in " , 为何要加空格呢,你加上和去掉比较一下就知道了 。 可以屏蔽掉不少保存到anr.log文件的无效信息 。

定位到关键的事件信息如下:

--------- switch to system
01-01 01:12:12.700506 359 395 W ProcessCpuTracker: Skipping unknown process pid 18163
01-01 01:12:12.703659 359 395 E ActivityManager: ANR in com.hsae.cn202sr.usb (com.hsae.cn202sr.usb/.music.MusicPlayActivity)
01-01 01:12:12.703659 359 395 E ActivityManager: PID: 902
01-01 01:12:12.703659 359 395 E ActivityManager: Reason: Input dispatching timed out (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: 5. Wait queue head age: 5622.3ms.)
01-01 01:12:12.703659 359 395 E ActivityManager: Load: 2.62 / 2.69 / 2.05
01-01 01:12:12.703659 359 395 E ActivityManager: CPU usage from 0ms to 6382ms later (2019-01-01 01:12:06.291 to 2019-01-01 01:12:12.674):

01-01 01:12:12.703659 359 395 E ActivityManager: 2.6% 250/media.codec: 2.6% user + 0% kernel / faults: 2 minor
01-01 01:12:12.703659 359 395 E ActivityManager: 2.6% 289/HwBinder:250_2: 2.6% user + 0% kernel
01-01 01:12:12.703659 359 395 E ActivityManager: 2.7% 259/logcatd: 0% user + 2.7% kernel
01-01 01:12:12.703659 359 395 E ActivityManager: 61% TOTAL: 28% user + 32% kernel + 0.5% iowait

在1分12秒的时候 ActivityManager 发生了如下错误:com.hsae.cn202sr.usb 包下面的MusicPlayActivity 无响应 。

原因:Reason: Input dispatching timed out

翻译:5分钟,10分钟,15分钟内的平均负载分别为:2.62 , 2.69 , 2.05

在这里我们大概知道问题是什么了,结合我们之前的操作流程,我们知道问题是在点击按钮某时候可能处理不过来按钮事件,导致超时无响应 。那么现在似乎已经可以进行工作了 。 我们知道Activity中是通过重载dispatchTouchEvent(MotionEvent ev)来处理点击屏幕事件 。 然后我们可以顺藤摸瓜,一点点分析去查找原因 。 但这样够了么 ?其实不够 , 至少我们不能准确的知道到底问题在哪儿 , 只是猜测 ,比如这个应用程序中,我就在顺藤摸瓜的时候发现了多个IO操作的地方都在主线程中,可能引起问题,但不好判断到底是哪个 ,所以我们目前掌握的信息还不够 。 于是我们再分析log文件中的traces.txt, 搜索“Dalvik Thread”关键词,快速定位到本应用程序的虚拟机信息日志,如下:

DALVIK THREADS (17):

“main” prio=5 tid=1 Native
| group=“main” sCount=1 dsCount=0 flags=1 obj=0x732dc000 self=0xb0204000
| sysTid=902 nice=-10 cgrp=default sched=0/0 handle=0xb3a344a8
| state=S schedstat=( 20251017391 5278422276 56303 ) utm=1552 stm=473 core=3 HZ=100
| stack=0xbe0c3000-0xbe0c5000 stackSize=8MB
| held mutexes=
native: #00 pc 00049618 /system/lib/libc.so (__ioctl+8)
native: #01 pc 0001e167 /system/lib/libc.so (ioctl+38)
native: #02 pc 00040bcf /system/lib/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+170)
native: #03 pc 00041589 /system/lib/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)+236)
native: #04 pc 0003bae1 /system/lib/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+36)
native: #05 pc 00063023 /system/lib/libmedia.so (???)
native: #06 pc 00063d31 /system/lib/libmedia.so (android::MediaMetadataRetriever::setDataSource(int, long long, long long)+72)
native: #07 pc 00028b6d /system/lib/libmedia_jni.so (???)
native: #08 pc 005e7dcf /system/framework/arm/boot-framework.oat (Java_android_media_MediaMetadataRetriever_setDataSource__Ljava_io_FileDescriptor_2JJ+126)
at android.media.MediaMetadataRetriever.setDataSource(Native method)
at android.media.MediaMetadataRetriever.setDataSource(MediaMetadataRetriever.java:69)
at com.hsae.imxplayer.core.MusicAndoridMediaPlayer.getArtist(MusicAndoridMediaPlayer.java:168)
at com.hsae.imxplayer.core.MusicHSAEMediaPlayer.getArtist(MusicHSAEMediaPlayer.java:454)
at com.hsae.cn202sr.usb.music.core.MusicPlayService.getArtistName(MusicPlayService.java:289)
at com.hsae.cn202sr.usb.music.core.MusicPlayControl.getArtist(MusicPlayControl.java:1203)
at com.hsae.cn202sr.usb.music.core.MusicPlayService.play(MusicPlayService.java:196)
at com.hsae.cn202sr.usb.music.core.MusicPlayControl.play(MusicPlayControl.java:528)
at com.hsae.cn202sr.usb.music.core.MusicPlayControl.doPlayPause(MusicPlayControl.java:274)
at com.hsae.cn202sr.usb.music.view.impl.MusicControlImpl 4. o n C l i c k ( M u s i c C o n t r o l I m p l . j a v a : 367 ) a t a n d r o i d . v i e w . V i e w . p e r f o r m C l i c k ( V i e w . j a v a : 6254 ) a t a n d r o i d . v i e w . V i e w 4.onClick(MusicControlImpl.java:367) at android.view.View.performClick(View.java:6254) at android.view.View 4.onClick(MusicControlImpl.java:367)atandroid.view.View.performClick(View.java:6254)atandroid.view.ViewPerformClick.run(View.java:24705)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6600)
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:772)

每一段都是一个线程 ,当然我们还是看线程号为1的主线程了。通过分析发现关键问题是这样:

at com.hsae.cn202sr.usb.music.view.impl.MusicControlImpl$4.onClick(MusicControlImpl.java:367)

定位到代码:

private final OnClickListener mPlayPauseListener = new OnClickListener() {
@Override
public void onClick(View v) {
if(!AppContext.getInstance().isUsbMounted()){
PopUpUtil.getInstance().popUpRemind(Util.POP_UNMOUNT_USB);
return;
}

  if (mPauseClickListener != null) {
     if (msc.isPlaying()) {
        mPauseClickListener.stopRotate();

} else {
mPauseClickListener.startRotate();
}
}
msc.doPlayPause();
}
};
很清楚了, msc.doPlayPause()方法之后执行时间太长的问题

你可能感兴趣的:(android)