摘要:監聽上報應用無響應是數據采集系統功能之一,本文講述一種可行實現方案。是一個用于監聽文件訪問創建修改刪除移動等操作的監聽器。為此本文同時提供一種線程輪詢措施,用于輔助監聽。
監聽上報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 MapmAttributes = 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....
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70236.html
摘要:錯誤使用單利在開發中單例經常需要持有對象,如果持有的對象生命周期與單例生命周期更短時,或導致無法被釋放回收,則有可能造成內存泄漏。如果集合是類型的話,那內存泄漏情況就會更為嚴重。 目錄介紹 1.OOM和崩潰優化 1.1 OOM優化 1.2 ANR優化 1.3 Crash優化 2.內存泄漏優化 2.0 動畫資源未釋放 2.1 錯誤使用單利 2.2 錯誤使用靜態變量 2.3 ...
閱讀 3932·2021-10-12 10:12
閱讀 2889·2021-09-10 11:18
閱讀 3677·2019-08-30 15:54
閱讀 2811·2019-08-30 15:53
閱讀 642·2019-08-30 13:54
閱讀 973·2019-08-30 13:21
閱讀 2263·2019-08-30 12:57
閱讀 1694·2019-08-30 11:10