Android如何监听anr,一种监听ANR方案

监听上报ANR(Application Not Responding,应用无响应)是数据采集系统功能之一,本文讲述一种可行实现方案。

方案概述

ANR一般有三种类型[1]:

KeyDispatchTimeout(5 seconds) --主要类型按键或触摸事件在特定时间内无响应

BroadcastTimeout(10 seconds) --BroadcastReceiver在特定时间内无法处理完成

ServiceTimeout(20 seconds) --小概率类型 Service在特定的时间内无法处理完成

当应用发生ANR,Android系统会将ANR Log输出至/data/anr/traces.txt。traces.txt中log stack有固定格式(见以下示例代码[2])。本文的方案就是监听traces.txt文件,对traces.txt内容作解析,对解析结果进行存储和上报;

----- pid 30307 at 2015-05-30 14:51:14 -----

Cmd line: com.example.androidyue.bitmapdemo

JNI: CheckJNI is off; workarounds are off; pins=0; globals=272

DALVIK THREADS:

(mutexes: tll=0 tsl=0 tscl=0 ghl=0)

"main" prio=5 tid=1 TIMED_WAIT

| group="main" sCount=1 dsCount=0 obj=0x416eaf18 self=0x416d8650

| sysTid=30307 nice=0 sched=0/0 cgrp=apps handle=1074565528

| state=S schedstat=( 0 0 0 ) utm=5 stm=4 core=3

at java.lang.VMThread.sleep(Native Method)

at java.lang.Thread.sleep(Thread.java:1044)

at java.lang.Thread.sleep(Thread.java:1026)

at com.example.androidyue.bitmapdemo.MainActivity$1.run(MainActivity.java:27)

at android.app.Activity.runOnUiThread(Activity.java:4794)

at com.example.androidyue.bitmapdemo.MainActivity.onResume(MainActivity.java:33)

at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1282)

at android.app.Activity.performResume(Activity.java:5405)

方案实现

监听文件在Android中有android.os.FileObserver[3]实现方式。FileObserver是一个用于监听文件访问、创建、修改、删除、移动等操作的监听器。FileObserver是理想方案,但在测试过程中发现很多设备不支持FileObserver方式。为此本文同时提供一种线程轮询措施,用于辅助ANR监听。以下列出几个重要的实现类。

/**

* A Observer to detect traces.txt CLOSE WRITE event

*

*/

final class ANRFileObserver extends FileObserver {

private IDetectCallback mIDetectCallback;

public ANRFileObserver(String path, IDetectCallback callback) {

super(path, FileObserver.CLOSE_WRITE);

this.mIDetectCallback = callback;

}

@Override

public void onEvent(int event, String path) {

if (path == null) {

return;

}

path = "/data/anr/" + path;

if (path.contains("trace") && mIDetectCallback != null) {

mIDetectCallback.onANR(path, false,System.currentTimeMillis());

}

}

}

/**

* loop traces.txt in 500MS

*

*/

final class ANRLoopRunnable implements Runnable {

private long lastModified;

private IDetectCallback mIDetectCallback;

private static final String ANR_PATH = "/data/anr/traces.txt";

private int loop = 0;

public ANRLoopRunnable(IDetectCallback callback) {

mIDetectCallback = callback;

}

@Override

public void run() {

if (mIDetectCallback == null) {

return;

}

if (!exists()) {

mIDetectCallback.onFileNotExist();

return;

}

if (!canRead()) {

return;

}

lastModified = lastModified();

while (!interrupted()) {

long last = lastModified();

if (last != lastModified) {

loop++;

trySleep(last);

continue;

} else {

if (loop > 0) {

mIDetectCallback.onANR(ANR_PATH,true, System.currentTimeMillis());

break;

} else {

trySleep(last);

continue;

}

}

}

}

private void trySleep(long last) {

lastModified = last;

try {

Thread.sleep(500);

} catch (InterruptedException e) {

}

}

/**

* 获取ANR文件最后一次更新时间

*

* @return

*/

private long lastModified() {

long time = 0L;

File localFile = new File(ANR_PATH);

if (localFile.exists()) {

time = localFile.lastModified();

}

return time;

}

private boolean canRead() {

boolean bool = false;

File localFile = new File(ANR_PATH);

if (localFile.exists()) {

bool = localFile.canRead();

}

return bool;

}

private boolean exists() {

File localFile = new File(ANR_PATH);

return localFile.exists();

}

}

/**

* An Item used to store traces info.`[4]`

*/

public class TracesItem {

/**

* Constant for output

*/

public static final String PID = "PID";

/**

* Constant for output

*/

public static final String APP = "APP";

/**

* Constant for output

*/

public static final String STACK = "STACK";

/**

* Constant for output

*/

public static final String STATE = "STATE";

private Map mAttributes = new HashMap();

/**

* Get the PID of the event.

*/

public Integer getPid() {

return (Integer) getAttribute(PID);

}

/**

* Set the PID of the event.

*/

public void setPid(Integer pid) {

setAttribute(PID, pid);

}

/**

* Get the reason of the event.

*/

public String getState() {

return (String) getAttribute(STATE);

}

/**

* Set the reason of the event.

*/

public void setState(String reason) {

setAttribute(STATE, reason);

}

/**

* Get the app or package name of the event.

*/

public String getApp() {

return (String) getAttribute(APP);

}

/**

* Set the app or package name of the event.

*/

public void setApp(String app) {

setAttribute(APP, app);

}

/**

* Get the stack for the crash.

*/

public String getStack() {

return (String) getAttribute(STACK);

}

/**

* Set the stack for the crash.

*/

public void setStack(String stack) {

setAttribute(STACK, stack);

}

/**

* Set an attribute to a value.

*

* @param attribute The name of the attribute.

* @param value The value.

* @throws IllegalArgumentException If the attribute is not in allowedAttributes.

*/

protected void setAttribute(String attribute, Object value) throws IllegalArgumentException {

mAttributes.put(attribute, value);

}

/**

* Get the value of an attribute.

*

* @param attribute The name of the attribute.

* @return The value or null if the attribute has not been set.

* @throws IllegalArgumentException If the attribute is not in allowedAttributes.

*/

protected Object getAttribute(String attribute) throws IllegalArgumentException {

return mAttributes.get(attribute);

}

}

/**

* A Parser to parse Android traces files. `[4]`

*/

public class TracesParser {

/**

* Matches: ----- pid PID at YYYY-MM-DD hh:mm:ss -----

*/

private static final Pattern PID = Pattern.compile(

"^----- pid (\\d+) at \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} -----$");

/**

* Matches: Cmd line: APP

*/

private static final Pattern APP = Pattern.compile("^Cmd ?line: (\\S+).*$");

/**

* Matches: "main" prio=5 tid=1 STATE

*/

private static final Pattern STACK = Pattern.compile("^\"main\" (prio=\\d+) (tid=\\d+) (.*)");

/**

* parse traces.txt

*

* @param path traces.txt

* @return

*/

public static TracesItem parse(String path) {

BufferedReader br = null;

FileReader fr = null;

TracesItem traces = new TracesItem();

StringBuffer stack = null;

String state = null;

try {

fr = new FileReader(path);

br = new BufferedReader(fr);

String sCurrentLine;

br = new BufferedReader(new FileReader(path));

while ((sCurrentLine = br.readLine()) != null) {

if (state == null) {

Matcher m = PID.matcher(sCurrentLine);

if (m.matches()) {

traces.setPid(Integer.parseInt(m.group(1)));

}

m = APP.matcher(sCurrentLine);

if (m.matches()) {

traces.setApp(m.group(1));

}

m = STACK.matcher(sCurrentLine);

if (m.matches()) {

state = m.group(3);

traces.setState(state);

}

} else if (!"".equals(sCurrentLine)) {

if (stack == null) {

stack = new StringBuffer();

}

stack.append(sCurrentLine);

stack.append("\n");

} else {

traces.setStack(stack.toString().trim());

return traces;

}

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (br != null)

br.close();

if (fr != null)

fr.close();

} catch (Exception ex) {

ex.printStackTrace();

}

}

if (stack == null) {

return null;

}

traces.setStack(stack.toString().trim());

return traces;

}

}

引用:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值