对于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. tail head命令

    显示1.txt的最后2行    tail -2 1.txt 显示1.txt的最后1行    tail -n 1 1.txt    tail -1 1.txt 显示1.txt的最后10行 tail 1. ...

  2. ArcEngine中使用上下左右键移动地图

    转自愿文ArcEngine中使用上下左右键移动地图 因项目需要,需对mapcontrol控件响应上下左右键,从网上找的方法都一样,都值提到了需要设置axMapControl1的KeyIntercept ...

  3. JSONP安全防范解决方案新思路

    jsonp安全性防范,分为以下几点:1. 防止callback参数意外截断js代码,特殊字符单引号双引号,换行符均存在风险2. 防止callback参数恶意添加标签(如script),造成XSS漏洞3 ...

  4. 微信小程序开发体验

    1.  申请小程序账号 小程序目前不支持个人申请,企业申请后填写基本信息 本来以为用原来公司申请的公众号就可以申请小程序权限,貌似不行 2.  添加开发者 管理员默认拥有开发者所有权限 添加其他开发者 ...

  5. java 加减乘除错误

    有次做一个for循环(1000次左右),做的事情也是很简单的事情,就是   Double testValue = (long类型 / 8 ) * long类型 的一些加减乘除操作, 但是总是出现一些数 ...

  6. OpenFileDialog

    打开一个文件         private void button1_Click(object sender, EventArgs e)         {             openFile ...

  7. linux文件的隐藏属性:chattr

    1. 文件的隐藏属性 linux除了9个权限外,还有些隐藏属性, 使用chattr命令来设置. 使用方法: $ chattr +-=[ASacDdIijsTtu] + : 添加一个特殊參数 - :   ...

  8. android 70 使用ListView把数据显示至屏幕

    使用单元测试添加数据: package com.itheima.showdata; import java.sql.ResultSet; import android.content.Context; ...

  9. 汇编语言-[BX]和loop指令

    汇编语言-[BX]和loop指令 [BX]指令介绍 mov ax,[bx] 功能: bx为偏移地址,段地址默认为ds.因此,上面指令作用就是将ax中的数据送入内存ds:bx处,即:((ds)*16 + ...

  10. iOS内存泄漏自动检测工具PLeakSniffer

    新款objective-C内存泄漏自动检测工具 PLeakSniffer , GitHub地址 (https://github.com/music4kid/PLeakSniffer). 背景 前些天读 ...