对于Android平台的工程师来说,ANR应该是每个人都会遇到的问题,因为导致它的原因有很多,例如在主线程进行耗时操作,调用大量cpu资源进行复杂的预算等,并且可能在大多数情况下,这类问题不会发生,只会在极端特殊的情况下暴露(例如很长时间的自动化脚本测试,monkey测试),所以我们必须得学会如何去分析这类问题,才能让模块的性能经得住考验。
一. 什么是ANR?为什么会有ANR发生?

如果当你进行一些操作之后,发现手机屏幕上出现类似上面的dialog,那么很不幸,你中招了。。。
ANR,Application Not Responding,即应用无响应。
一般来说,当应用对用户的交互没有反应时,系统就会弹出上述的ANR dialog。这种情况一般发生在如主线程被IO操作block住了,主线程进行了大量的例如读取数据库的操作等。
从google官方文档上介绍来看,主要由以下两种情况引起:
1. 应用在5秒内对于用户的输入事件无响应
2. BroadcastReceiver在10秒内不能完成onReceive()方法的执行
Note: 上述的时间是基于Google原生的Code,国内不少厂商因为某些原因会把这些时间延长,请以具体的vendor代码为准

二. NFC为什么会有ANR问题发生
首先和没接触过NFC的朋友介绍下NFC。
NFC,Near field communicatwww.yghrcp88.cn ion,即近场通讯,是由非接触式射频识别(RFID)演变而来的短距离无线电技术,由Nokia, Sony, NXP共同研发。在国外,如日本,这种技术运用的已经十分广泛,无论从出行到购物,哪里都有Felica(日本使用的NFC标准)的身影。然而国内因为某宝过于强大和人性化,NFC技术推动任重而道远。但是随着如小米钱包等应用开始使用NFC来模拟公交卡以及银行卡方便用户的生活,个人认为,未来是美好的!!!
作为一个Local Connectivity的重要模块,NFC不仅可以进行Read/Write Tag,而且可以通过Android Beam(Android 4.0开始支持的点对点传输的feature)传输文件。handover的功能更是让NFC成为一个wifi和bt快速建立链接的桥梁,极大的方便了用户的近距离传输的需求。也正是因为这些原因,在特殊情况下的并发操作,就会导致NFC出现ANR的问题,下面以一个简单的NFC相互调用死锁导致的ANR案例进行分析。

三. 案例分析
首先推荐给各位一个查看源代码的网站,http://androidxref.com/, 如果没有VPN的话,这个网站看源码还是比较给力的,可能大多数哥们都知道,呵呵~

下面先简单介绍下导致ANR发生的操作:
在NFC关闭的情况下,(Android Beam必须是随着NFC的关闭自动关闭的,否则因为相应的component被disable,分享列表中找不到Android Beam选项),通过Android Beam去分享一个文件,这种情况下会弹出一个提示需要开启NFC功能的Dialog,点击确定,正常情况下,会出现Android Beam的图片缩放界面如下图,但是ANR发生时整个界面没有任何反应,几秒钟后,系统就会弹出Settings ANR的dialog。

对于ANR问题的分析,我们应该首先去找问题发生时,手机自动保存在data/anr目录下的trace.txt文件,这是最能直观反应问题发生时堆栈的信息以及各种资源的使用情况。
因为NfcService是NFC上层最核心的一个文件,底层的所有处理都会一层层往上抛给NfcService,上层的API接口也只会通过NfcService去调用具体的底层实现。所有我们现在trace.txt中以NfcService作为关键字进行搜索,看到如下trace.log
[plain] view plain copy
"Binder_1" prio=5 tid=8 Blocked(prio 进程号, tid 线程号)
| group="main" sCount=1 dsCount=0 obj=0x12c8b0a0 self=0x7f8ef53400
| sysTid=2903 nice=0 cgrp=default sched=0/0 handle=0x7f93af5440
| state=S schedstat=( 81420425 126587653 792 ) utm=3 stm=5 core=0 HZ=100
| stack=0x7f939f9000-0x7f939fb000 stackSize=1013KB
| held mutexes=
at com.android.nfc.NfcService$NfcAdapterService.getState(NfcService.java:1813)
- waiting to lock <0x0196c7d2> (a com.android.nfc.NfcService) held by thread 18 --->被线程18阻塞
at android.nfc.INfcAdapter$Stub.onTransact(INfcAdapter.java:95)
at android.os.Binder.execTransact(Binder.java:477)</span>

从上面的trace log可以看到,NfcService$NfcAdapterService.getState()想获得0x0196c7d2,即NfcService对象锁,代码如下
[java] view plain copy
@Override
public int getState() throws RemoteException {
synchronized (NfcService.this) {
return mState;
}
}
但是这个对象锁并不能马上获得,因为thread 18正在占用,看下线程18的堆栈信息
[plain] view plain copy
"Binder_3" prio=5 tid=18 Blocked
| group="main" sCount=1 dsCount=0 obj=0x12e120a0 self=0x7f94114200
| sysTid=3414 nice=0 cgrp=default sched=0/0www.wx1677.com/  handle=0x7f7c9a0440
| state=S schedstat=( 67563697 66692461 439 ) utm=0 stm=6 core=0 HZ=100
| stack=0x7f7c8a4000-0x7f7c8a6000 stackSize=1013KB
| held mutexes=
at com.android.nfc.P2pLinkManager.isLlcpActive(P2pLinkManager.java:410)
- waiting to lock <0x0c8140a3> (a com.android.nfc.P2pLinkManager) held by thread 1 --->被线程1阻塞
at com.android.nfc.NfcService$NxpExtrasService._open(NfcService.java:3173)
- locked <0x0196c7d2> (a com.android.nfc.NfcService)
at com.android.nfc.NfcService$NxpExtrasService.open(NfcService.java:3149)
at com.nxp.intf.INxpExtrasService$Stub.onTransact(INxpExtrasService.java:55)
at android.os.Binder.execTransact(Binder.java:477)
上面的log可以看出,NfcService的对象锁正在被NfcService$NxpExtrasService._open()方法所持有,代码如下:
[java] view plain copy
private int _open(IBinder b) {
synchronized(NfcService.this) {
if (!isNfcEnabled()) {
return EE_ERROR_NFC_DISABLED;
}
if (mInProvisionMode) {
// Deny access to the NFCEE as long as the device is being setup
return EE_ERROR_IO;
}
if (mP2pLinkManager.isLlcpActive()) {
// Don't allow PN544-based devices to open the SE while the LLCP
// link is still up or in a debounce state. This avoids race
// conditions in the NXP stack around P2P/SMX switching.
return EE_ERROR_EXT_FIELD;
}
上面的这个方法迟迟不能执行完毕,是因为调用了mP2pLinkManager.isLlcpActive(),这个方法希望获得0x0cwww.zhenlyule.cn/ 8140a3,即P2pLinkManager的对象锁,但是这个对象锁也不能马上获得,正在被thread1挂起。
[java] view plain copy
public boolean isLlcpActive() {
synchronized (this) { ---> P2pLinkManager对象锁
return mLinkState != LINK_STATE_DOWN;
}
}
那么我们继续看下thread1的trace信息:
[plain] view plain copy
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 dsCount=0www.myqunliphoto.com  obj=0x762a8fb8 self=0x7f955fba00
| sysTid=2880 nice=0 cgrp=default sched=0/0 handle=0x7f98da8fe8
| state=S schedstat=( 344423025 553179190 922 ) utm=24 stm=10 core=3 HZ=100
| stack=0x7fca15d000-0x7fca15f000 stackSize=8MB
| held mutexes=
at com.android.nfc.NfcService.playSound(NfcService.java:1509)
- waiting to lock <0x0196c7d2> (a com.android.nfc.NfcService) held by thread 18
at com.android.nfc.P2pEventManager.onP2pNfcTapRequested(P2pEventManager.java:81)
at com.android.nfc.P2pLinkManager.onManualBeamInvoke(P2pLinkManager.java:455)
- locked <0x0c8140a3> (a com.android.nfc.P2pLinkManager)
at com.android.nfc.NfcService$NfcServiceHandler.handleMessage(NfcService.java:4366)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5541)
at java.lang.reflect.Method.invoke!(Native method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:935)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:726)
0x0c8140a3这个锁正在被P2pLinkManager.onManualBeamInvoke()方法占用,代码如下:
[java] view plain copy
public void onManualBeamInvoke(BeamShareData shareData) {
synchronized (P2pLinkManager.this) {
if (mLinkState != LINK_STATE_DOWN) {
return;
}
if (mForegroundUtils.getForegroundUids().contains(mNdefCallbackUid)) {
// Try to get data from the registered NDEF callback
prepareMessageToSend(false);
} else {
mMessageToSend = null;
mUrisToSend = null;
}
if (mMessageToSend == null && mUrisToSend == null && shareData != null) {
// No data from the NDEF callback, get data from ShareData
if (shareData.uris != null) {
mUrisToSend = shareData.uris;
} else if (shareData.ndefMessage != null) {
mMessageToSend = shareData.ndefMessage;
}
mUserHandle = shareData.userHandle;
}
if (mMessageToSend != null ||
(mUrisToSend != null && mHandoverDataParser.isHandoverSupported())) {
mSendState = SEND_STATE_PENDING;
mEventListener.onP2pNfcTapRequested();
scheduleTimeoutLocked(MSG_WAIT_FOR_LINK_TIMEOUT, WAIT_FOR_LINK_TIMEOUT_MS);
}
}
}

上面的方法里会去调用mEventListener.onP2pNfcTapRequested(),代码如下:
[java] view plain copy
@Override
public void onP2pNfcTapRequested() {
mNfcService.playSound(NfcService.SOUND_START);
mNdefSent = false;
mNdefReceived = false;
mInDebounce = false;

mVibrator.vibrate(VIBRATION_PATTERN, -1);
这个方法会调用NfcService里面的mNfcService.playSound()。trace log显示playSound()会去想持有0x0196c7d2即NfcService对象。看下代码是不是这样:
[java] view plain copy
public void playSound(int sound) {
synchronized (this) { ---> NfcService对象锁
if (mSoundPool == null) {
Log.w(TAG, "Not playing sound when NFC is disabled");
return;
}
这里请注意,上面NfcService$NxpExtrasService._open()正在持有的对象也是这个。
现在基本知道什么情况下,我们回过头来再捋一捋。
NfcAdapterService.getState希望持有NfcService对象,无法获得block
NfcService对象正在被NxpExtrasService._open()持有, 这个方法无法执行完毕,被mP2pLinkManager.isLlcpActive() block
mP2pLinkManager.isLlcpActive()希望持有P2pLinkManager的对象,无法获得 block
P2pLinkManager对象正在被onManualBeamInvoke()方法持有,这个方法无法执行完毕,被mEventListener.onP2pNfcTapRequested() block
mEventListener.onP2pNfcTapRequested() 无法执行完毕,被mNfcService.playSound() block
mNfcService.playSound() 希望持有NfcService的对象,这个对象被最上面的NxpExtrasService._open()持有

所以总体来说,就是正在占用NfcService锁的NxpExtrasService._open()需要P2pLinkManager的锁释放,而正在占用P2pLinkManager这个锁的onManualBeamInvoke()方法需要NfcService的锁释放,双方互不让步,造成死锁。

暂时想到的解决的策略就是将
[java] view plain copy
public void playSound(int sound) {
synchronized (this) { <---> NfcService对象锁
if (mSoundPool == null) {
Log.w(TAG, "Not playing sound when NFC is disabled");
return;
}
这个锁的范围缩小,换成一个私有锁。
即 Object mPlaySoundLock = new Obejct(),然后将this替换成mPlaySoundLock即可。

一. 什么是ANR?为什么会有ANR发生?的更多相关文章

  1. 转如何分析解决Android ANR

    一:什么是ANR ANR:Application Not Responding,即应用无响应 二:ANR的类型 ANR定义:在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示 ...

  2. Android ANR分析(1)

    转自:http://blog.csdn.net/itachi85/article/details/6918761 一:什么是ANR ANR:Application Not Responding,即应用 ...

  3. 死锁 android ANR

    以下为一段ANR的LOG,主要是在WindowManagerService.java和ActivityManagerService.java中实现. W/WindowManager( 2183): K ...

  4. ANR的一个实例分析

    ANR是android经常出的超时提示,以前看过一个帖子,内容是mediaplayer在release的时候出的ANR,作者也是出了方法,什么加handler之类的. 最后都么有解决,咱们先看看那位同 ...

  5. 【Android】定位与解决anr错误记录

    问题描写叙述 cocos2d-x游戏项目androidproject接入sdk.支付成功后,java代码回调lua方法.产生了anr. 怎样定位anr? watermark/2/text/aHR0cD ...

  6. 谈谈 ANR 之 Service 超时

    1. 核心源码 关键类 路径(/frameworks/base/) ActiveServices.java services/core/java/com/android/server/am/Activ ...

  7. 【朝花夕拾】Android性能篇之(八)ANR篇--草稿

    1.ANR概念 2.ANR发生场景 Android开发者官网 上说到了两个原因:(1)点击按键或者触摸屏幕等输入事件在5s内没有响应:(2)10s内没有完成广播事件.如下所示: Android wil ...

  8. [转]ANR问题分析指南

    引言 每天收到无数的兄弟团队的同事向系统转ANR JIRA,有些一旦遇到App ANR就直接转到系统组,有些简单看一下就转到系统组帮忙看一下.如此浩瀚的JIRA,我们什么事不做也处理不过来,请每个Ap ...

  9. 遇到ANR问题的处理步骤

    遇到ANR问题的处理步骤 问题描述 开发中难免会遇到ANR的问题,遇到ANR问题不要想着是因为设备的卡顿出现的问题,我们无法解决,我们应先找到导致ANR的原因,分析原因之后,再来判断这个问题可不可以解 ...

随机推荐

  1. Linux常用命令及vim的使用、vim常用插件(推荐)

    看了3篇文章,很好 vim中的区域拷贝 剪切,粘贴: 正常模式,移动光标到剪切的区域开始处,按v,进入可视模式,然后选择区域.按x键,剪切.到指定位置按p粘贴. 撤销按u 恢复按ctrl-r  简明 ...

  2. 【转】如何实现一个malloc

    任何一个用过或学过C的人对malloc都不会陌生.大家都知道malloc可以分配一段连续的内存空间,并且在不再使用时可以通过free释放掉.但是,许多程序员对malloc背后的事情并不熟悉,许多人甚至 ...

  3. Linux Foundation Secure Boot System Released

    As promised, here is the Linux Foundation UEFI secure boot system.  This was actually released to us ...

  4. 337. House Robber III

    二刷吧..不知道为什么house robbery系列我找不到笔记,不过印象中做了好几次了. 不是很难,用的post-order做bottom-up的运算. 对于一个Node来说,有2种情况,一种是选( ...

  5. CodeForces - 269C Flawed Flow

    http://codeforces.com/problemset/problem/269/C 题目大意: 给定一个边没有定向的无法增广的残量网络且1是源点,n是汇点,给定每条边中的流.  让你把所有边 ...

  6. spring applicationContext.xml 文件

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  7. C# 自己对delegate的总结和认识

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  8. [Webpack 2] Hashing with Webpack for long term caching

    Leveraging the browser cache is an important part of page load performance. A great way to utilize t ...

  9. [PHP] csv to xml

    <?php error_reporting(E_ALL | E_STRICT); ini_set('display_errors', true); ini_set('auto_detect_li ...

  10. android Camera拍照 及 MediaRecorder录像 预览图像差90度

    Camera拍照: 今天做照相机程序,结果写好了发现出问题了,预览的图像差90度.相关源代码如下: Camera.Parameters params = camera.getParameters(); ...