资讯专栏INFORMATION COLUMN

一种监听ANR方案

winterdawn / 1688人阅读

摘要:监听上报应用无响应是数据采集系统功能之一,本文讲述一种可行实现方案。是一个用于监听文件访问创建修改删除移动等操作的监听器。为此本文同时提供一种线程轮询措施,用于辅助监听。

监听上报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("
");
                        } 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;
            }
        
        }
        
    
引用:

[1] ANR简介以及解决方案 : http://www.snowdream.tech/201...
[2] 说说Android中的ANR:http://droidyue.com/blog/2015...
[3] FileObserver类:https://developer.android.com...
[4] loganalysis:https://android.googlesource....

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70236.html

相关文章

  • 一种监听ANR方案

    摘要:监听上报应用无响应是数据采集系统功能之一,本文讲述一种可行实现方案。是一个用于监听文件访问创建修改删除移动等操作的监听器。为此本文同时提供一种线程轮询措施,用于辅助监听。 监听上报ANR(Application Not Responding,应用无响应)是数据采集系统功能之一,本文讲述一种可行实现方案。 方案概述 ANR一般有三种类型[1]: KeyDispatchTimeout(5...

    FullStackDeveloper 评论0 收藏0
  • Android优化总结

    摘要:错误使用单利在开发中单例经常需要持有对象,如果持有的对象生命周期与单例生命周期更短时,或导致无法被释放回收,则有可能造成内存泄漏。如果集合是类型的话,那内存泄漏情况就会更为严重。 目录介绍 1.OOM和崩溃优化 1.1 OOM优化 1.2 ANR优化 1.3 Crash优化 2.内存泄漏优化 2.0 动画资源未释放 2.1 错误使用单利 2.2 错误使用静态变量 2.3 ...

    sunsmell 评论0 收藏0
  • 金九银十中,看看这31道Android面试题

    摘要:静态集合类引起内存泄露主要是,等,如果是静态集合这些集合没有及时的话,就会一直持有这些对象。关于合理使用内存,其实就是避免内存泄露中已经说明。参数原生参数元素需要支持机制参考进程线程管理一消息机制的框架这个系类。 阅读目录 1.如何对 Android 应用进行性能分析 2.什么情况下会导致内存泄露 3.如何避免 OOM 异常 4.Android 中如何捕获未捕获的异常 5.ANR 是...

    call_me_R 评论0 收藏0
  • ANR详细介绍

    摘要:使用或者时,调用设置优先级,否则仍然会降低程序响应,因为默认的优先级和主线程相同。使用处理工作线程结果,而不是使用或者来阻塞主线程。目录介绍 1.ANR简单介绍 2.ANR发生场景 3.ANR发生的原理 4.ANR有哪些具体案例 5.ANR具体如何分析 6.解决方案 7.ANR问题解答 好消息 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,P...

    番茄西红柿 评论0 收藏0
  • ANR详细介绍

    摘要:使用或者时,调用设置优先级,否则仍然会降低程序响应,因为默认的优先级和主线程相同。使用处理工作线程结果,而不是使用或者来阻塞主线程。目录介绍 1.ANR简单介绍 2.ANR发生场景 3.ANR发生的原理 4.ANR有哪些具体案例 5.ANR具体如何分析 6.解决方案 7.ANR问题解答 好消息 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,P...

    wslongchen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<