使用ddmlib实现android 性能监控
16lz
2021-01-23
使用ddmlib实现android 性能监控
原理:
cpu
adb shell dumpsys cpuinfo packageName
memory
adb shell dumpsys meminfo packageName
流量
cat /proc/uid_stat/uidxxx/tcp_rcv
cat /proc/uid_stat/uidxxx/tcp_snd
其中 uid的获得通过
adb shell dumpsys package packageName
取得 userId=(\d+)\s
FPS刷新频率
这个计算稍微复杂,请参见源码:
sdk/sources/android-18/com/android/uiautomator/platform/SurfaceFlingerHelper.java(以android-18中的路径为例,其他版本中也有该文件,可能路径不同)
我写了个GUI
请联系 lizejun_qd@126.com,试用~
核心代码实现
控制器
public class AdbController { protected static Logger logger = LoggerFactory.getLogger(AdbController.class); protected static AndroidDebugBridge adb = null; private static boolean isInit = false; public AdbController() { init(); } public void init() { if(!isInit) { AndroidDebugBridge.init(true); DdmPreferences.setTimeOut(20000); // adb = AndroidDebugBridge.createBridge(); //使用该方式,在没有启动adb时,会提示找不到 adb = AndroidDebugBridge.createBridge(ConfigurationSettings.ADB_PATH, false); isInit = true; } waitDeviceList(adb); } private void waitDeviceList(AndroidDebugBridge bridge) { int count = 0; while (bridge.hasInitialDeviceList() == false) { try { Thread.sleep(100); count++; } catch (InterruptedException e) { } if (count > 100) { System.out.println("Fail to Init Device list"); break; } }}public void terminate() { if(deviceTimer!= null){ deviceTimer.clearService(); } if(deviceListener!=null) AndroidDebugBridge.removeDeviceChangeListener(deviceListener); AndroidDebugBridge.terminate();}public IDevice[] getDevices() { return adb.getDevices();}public IDevice getCurrentDevice() { return deviceListener.mCurrentDevice;}public IDevice getDevice(String serialNum) { IDevice[] devices = adb.getDevices(); int nums = devices.length; for (int i = 0; i < nums; i++) { String curSerialNum = devices[i].getSerialNumber(); if (serialNum.equals(curSerialNum)) return devices[i]; } return null;}public String getCurrentActivity(String monitorPackage){ SFActivityService sf = new SFActivityService(getCurrentDevice(), monitorPackage); sf.executeCmd(); return sf.getCurActivity();}}
CPU
/** * 使用 dumpsys cpuinfo xxx.package.name 获取Cpu的性能指标 * @author zejun.lzj * */public class CpuInfoService extends Observable implements AdbShellService {protected IDevice device;private CpuInfoReceiver receiver = null;protected String monitorPackage;public CpuInfoService(IDevice device,String monitorPackage) { this.device = device; this.monitorPackage = monitorPackage; receiver = new CpuInfoReceiver(); }public Float getCpuRatio(){ Float cpuRatio = receiver.getCpuRatio(); if(cpuRatio == null || cpuRatio <0 ) cpuRatio = -1f; return cpuRatio;}public void executeCmd() { if(StringUtils.isEmpty(monitorPackage)) return; String cmd = "dumpsys cpuinfo " + monitorPackage; try { device.executeShellCommand(cmd, receiver); } catch (TimeoutException e) { logger.error(LOG_TAG,"TimeoutException"); e.printStackTrace(); } catch (AdbCommandRejectedException e) { logger.error(LOG_TAG,"AdbCommandRejectedException"); e.printStackTrace(); } catch (ShellCommandUnresponsiveException e) { logger.error(LOG_TAG,"ShellCommandUnresponsiveException"); e.printStackTrace(); } catch (IOException e) { logger.error(LOG_TAG,"IOException"); e.printStackTrace(); } Float ratio = getCpuRatio(); if(ratio >=0.0){ setChanged(); notifyObservers(ratio); }}public void stop() { receiver.setCanceledFlag(true); }private class CpuInfoReceiver extends MultiLineReceiver { private Pattern CPUINFO_MATCHER; private Float mCpuRatio = null; protected Boolean isCanceled = false; public CpuInfoReceiver() { super(); CPUINFO_MATCHER = Pattern.compile(".*"+ monitorPackage); } public void setCanceledFlag(Boolean isCanceled) { this.isCanceled = isCanceled; } public Float getCpuRatio() { return mCpuRatio; } public boolean isCancelled() { return isCanceled; } @Override public void processNewLines(String[] lines) { for (String line : lines) { Matcher cpuMatch = CPUINFO_MATCHER.matcher(line); if (cpuMatch.find()) { try { mCpuRatio = Float.parseFloat(cpuMatch.group().split("%")[0].trim()); break; } catch (NumberFormatException e) { System.out.println(String.format("Failed to parse %s as an integer", cpuMatch.group(1))); } } } }}public void clear() {}}
内存
public class MemInfoService extends Observable implements AdbShellService {protected IDevice device;private MemInfoReceiver receiver = new MemInfoReceiver();protected String monitorPackage;public MemInfoService(IDevice device,String monitorPackage) { this.device = device; this.monitorPackage = monitorPackage; this.addObserver(DataManager.getInstance().getMemObserver());}/** * 从Receive中拿到内存数据 * @return */public Float getMemInfo(){ Float memInfo = receiver.getMemInfo(); if(memInfo == null || memInfo <0 ) memInfo = -1f; return memInfo/1000;}public Float getDalvInfo(){ Float dalvInfo = receiver.getDalvInfo(); if(dalvInfo == null || dalvInfo <0 ) dalvInfo =-1f; return dalvInfo/1000;}public Float getNativeInfo () { Float nativeInfo = receiver.getNativeInfo(); if (nativeInfo == null || nativeInfo < 0) nativeInfo = -1f; return nativeInfo / 1000;}public void executeCmd() { if(StringUtils.isEmpty(monitorPackage)) return; String cmd = "dumpsys meminfo " + monitorPackage; try { device.executeShellCommand(cmd, receiver); } catch (TimeoutException e) { logger.error(LOG_TAG,"TimeoutException"); e.printStackTrace(); } catch (AdbCommandRejectedException e) { logger.error(LOG_TAG,"AdbCommandRejectedException"); e.printStackTrace(); } catch (ShellCommandUnresponsiveException e) { logger.error(LOG_TAG,"ShellCommandUnresponsiveException"); e.printStackTrace(); } catch (IOException e) { logger.error(LOG_TAG,"IOException"); e.printStackTrace(); } Float ratio1 = getMemInfo(); Float ratio2 = getDalvInfo(); Float ratio3 = getNativeInfo(); if(ratio1>0 && ratio2>0 && ratio3 >0){ setChanged(); notifyObservers(new DataManager.MEM_DATA(ratio1,ratio2,ratio3)); }}private static final class MemInfoReceiver extends MultiLineReceiver { protected Boolean isCanceled = false; public MemInfoReceiver() { super(); } public void setCanceledFlag(Boolean isCanceled) { this.isCanceled = isCanceled; } public Float getMemInfo() { return memInfo; } public Float getDalvInfo() { return dalvInfo; } public Float getNativeInfo() { return nativeInfo; } private static final String DALVIK_MATCHER = "Dalvik"; private static final String NATIVE_MATCHER = "Native"; private static final String TOTAL_MATCHER = "TOTAL"; protected Float memInfo = null; protected Float dalvInfo = null; protected Float nativeInfo = null; public boolean isCancelled() { return isCanceled; } private List tem = new ArrayList(); @Override public void processNewLines(String[] lines) { for(String line:lines) { //将输出的数据缓存起来 tem.add(line); if(line.contains(TOTAL_MATCHER)) { getMemInfo(tem); tem.clear(); } } } public void getMemInfo(List lines) { //这里使用的arrNative[index] ,index会有不同,原因是不同的版本输出的信息,有的叫Dalvik,有得叫Dalvik Heap for (String line : lines) { if (line.contains(NATIVE_MATCHER)) { String[] arrNative = line.split("\\s+"); if (9 == arrNative.length) { nativeInfo = Float.parseFloat(arrNative[7]); } else if ( 8 == arrNative.length) { nativeInfo = Float.parseFloat(arrNative[6]); } else if ( 7 == arrNative.length) { nativeInfo = Float.parseFloat(arrNative[5]); } continue; } if(line.contains(DALVIK_MATCHER)){ String[] arrDalvik = line.split("\\s+"); if (9 == arrDalvik.length) { dalvInfo = Float.parseFloat(arrDalvik[7]); } else if (8 == arrDalvik.length) { dalvInfo = Float.parseFloat(arrDalvik[6]); } else if ( 7 == arrDalvik.length) { dalvInfo = Float.parseFloat(arrDalvik[5]); } continue; } if(line.contains(TOTAL_MATCHER)){ String arrTotal[] = line.split("\\s+"); if (8 == arrTotal.length) { memInfo = Float.parseFloat(arrTotal[6]); }else if ( 7 == arrTotal.length) { memInfo = Float.parseFloat(arrTotal[5]); } break; } } }}public void stop() { receiver.setCanceledFlag(true); }public void clear() {}}
FPS
/** * dumpsys SurfaceFlinger --latency */public class SFLatencyService extends Observable implements AdbShellService{ private static String FRAME_LATENCY_CMD = "dumpsys SurfaceFlinger --latency"; protected IDevice device; private String monitorPackage; private String windowName;protected Logger logger = LoggerFactory.getLogger(this.getClass());private static final String LOG_TAG = "[SFLatencyService]-";public SFLatencyService(IDevice device,String monitorPackage) { this.device = device; this.monitorPackage = monitorPackage; clearSFBuffer(); receiver = new LatencyReceiver(this); this.addObserver(DataManager.getInstance().getFpsObserver());}private LatencyReceiver receiver = null;private Float laterFps;public Float getLaterFps() { return laterFps;}public void setLaterFps(Float laterFps) { this.laterFps = laterFps; if(laterFps>0){ setChanged(); notifyObservers(new DataManager.FPS_DATA(windowName,laterFps)); }}public String getActivity(){ SFActivityService sf = new SFActivityService(device,monitorPackage); sf.executeCmd(); return sf.getCurActivity();}public void clearSFBuffer(){ SFClearService sc = new SFClearService(device); sc.executeCmd();}public void executeCmd() { if(StringUtils.isEmpty(monitorPackage)) return; windowName = getActivity(); String command = FRAME_LATENCY_CMD; command = String.format("%s %s", FRAME_LATENCY_CMD, windowName); try { device.executeShellCommand(command,receiver); } catch (TimeoutException e) { logger.error(LOG_TAG,"TimeoutException"); e.printStackTrace(); } catch (AdbCommandRejectedException e) { logger.error(LOG_TAG,"AdbCommandRejectedException"); e.printStackTrace(); } catch (ShellCommandUnresponsiveException e) { logger.error(LOG_TAG,"ShellCommandUnresponsiveException"); e.printStackTrace(); } catch (IOException e) { logger.error(LOG_TAG,"IOException"); e.printStackTrace(); }}public void stop() { }private static final class LatencyReceiver extends MultiLineReceiver { SFLatencyService parent; public LatencyReceiver(SFLatencyService parent) { this.parent = parent; } protected Float fps = 0f; private static int BUFFER_SIZE = 128; private static int BUFFER_NUMBER = 3; /* An array list which includes the raw buffer information from frame latency tool */ private static List> mFrameBufferData = new ArrayList>(BUFFER_SIZE); /* Record the refresh period returned from driver */ //private static long mRefreshPeriod = -1; // Symbol of unfinished frame time */ public static final String PENDING_FENCE_TIME = new Long(Long.MAX_VALUE).toString(); private static final Pattern CHECK_MATCHER = Pattern.compile("^[\\d\\s]+$"); public boolean isCancelled() { return false; } public void clearBuffer(){ mFrameBufferData.clear(); } @Override public void processNewLines(String[] lines) { if(lines.length<2) return; for(String line:lines){ Matcher matcher = CHECK_MATCHER.matcher(line); if(!matcher.matches()) continue; String[] bufferValues = line.split("\\s+"); if(bufferValues.length==1){ if(line.trim().isEmpty()) continue; if(mFrameBufferData.isEmpty()) { //mRefreshPeriod = Long.parseLong(line.trim()); continue; } else { fps = (float)getFrameRate(); parent.setLaterFps(fps); //mRefreshPeriod = Long.parseLong(line.trim()); clearBuffer(); continue; } } if(bufferValues.length!=BUFFER_NUMBER) return; if (bufferValues[0].trim().compareTo("0") == 0) { continue; } else if (bufferValues[1].trim().compareTo(PENDING_FENCE_TIME) == 0 ) { System.out.println(LOG_TAG + "the data contains unfinished frame time"); continue; } List delayArray = Arrays.asList(bufferValues); mFrameBufferData.add(delayArray); } } /** * Calculate frame rate * @return */ public static double getFrameRate() { int mFrameLatencySampleSize = mFrameBufferData.size() - 1; long startTime = Long.parseLong(mFrameBufferData.get(0).get(1)); long endTime = Long.parseLong(mFrameBufferData.get(mFrameLatencySampleSize).get(1)); long totalDuration = endTime - startTime; return (double)((mFrameLatencySampleSize - 1) * Math.pow(10, 9))/totalDuration; }}public void clear() { if(receiver!=null) receiver.clearBuffer();}}
流量
public class TrafficInfoService extends Observable implements AdbShellService {protected IDevice device;protected String uid = null;private TrafficReceiver receiver;private String monitorPackage;Integer rcv = null;Integer snd = null;Integer flow = null;private static Integer firstRcv = null;private static Integer firstSnd = null;public Integer getRcv() { return rcv;}public Integer getSnd() { return snd;}public Integer getFlow() { return flow;}public TrafficInfoService(IDevice device, String monitorPackage) { this.device = device; receiver = new TrafficReceiver(); this.monitorPackage = monitorPackage; this.addObserver(DataManager.getInstance().getFlowObserver());} protected String getUid(){ UidService service = new UidService(device,monitorPackage); service.executeCmd(); return service.getUid();}public void executeCmd(){ if(StringUtils.isEmpty(monitorPackage)) return; if(uid == null) uid = getUid(); String cmd = String.format("cat /proc/uid_stat/%s/tcp_rcv;cat /proc/uid_stat/%s/tcp_snd", uid,uid); try { device.executeShellCommand(cmd, receiver); } catch (TimeoutException e) { logger.error(LOG_TAG,"TimeoutException"); e.printStackTrace(); } catch (AdbCommandRejectedException e) { logger.error(LOG_TAG,"AdbCommandRejectedException"); e.printStackTrace(); } catch (ShellCommandUnresponsiveException e) { logger.error(LOG_TAG,"ShellCommandUnresponsiveException"); e.printStackTrace(); } catch (IOException e) { logger.error(LOG_TAG,"IOException"); e.printStackTrace(); } notifyData();}public void notifyData(){ rcv = receiver.getRcv(); snd = receiver.getSnd(); if(rcv <0 || snd<0) return; if(firstRcv == null) firstRcv = rcv; if(firstSnd == null) firstSnd = snd; rcv = rcv - firstRcv; snd = snd - firstSnd; flow = rcv + snd; setChanged(); notifyObservers(new DataManager.FLOW_DATA(flow,snd,rcv));}public void stop() { receiver.setCanceledFlag(true); firstRcv = null; firstSnd = null;}private static final class TrafficReceiver extends MultiLineReceiver { private Integer mrcv = null; private Integer msnd = null; protected Boolean isCanceled = false; public void setCanceledFlag(Boolean isCanceled) { this.isCanceled = isCanceled; } public Integer getRcv() { if(mrcv == null || mrcv <0) mrcv = -1; return mrcv; } public Integer getSnd() { if(msnd == null || msnd <0) msnd = -1; return msnd; } public boolean isCancelled() { return isCanceled; } @Override public void processNewLines(String[] lines) { //System.out.println(lines[0]); if(lines.length<2) return; try{ mrcv = Integer.parseInt(lines[0].trim())/1000; msnd = Integer.parseInt(lines[1].trim())/1000; } catch (NumberFormatException e) { System.out.println(LOG_TAG + String.format(":Failed to parse %s to traffic", lines[0]+lines[1])); } }}public void clear() { firstRcv = null; firstSnd = null;}}
更多相关文章
- 性能优化学习资源
- Android性能检测--traceview工具各个参数的意思
- traceview进行Android性能测试
- Android 性能监控与分析方法
- android ui 布局性能优化
- 收藏Android下bitmap内存限制OUT OF MEMORY的方法
- Android 内存管理 &Memory Leak & OOM 分析
- [置顶] Android防止内存溢出浅析
- 且谈Android内存溢出