使用ddmlib实现android 性能监控

使用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

请联系 [email protected],试用~

核心代码实现

控制器

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<String> tem = new ArrayList<String>();
    @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<String> 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<List<String>> mFrameBufferData = new ArrayList<List<String>>(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<String> 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;
}

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-30 12:19:43

使用ddmlib实现android 性能监控的相关文章

Android性能监控

Android性能监控 一.搭建Android性能测试环境,参见<Android性能测试之Monkey使用>中内容. 二.启动Android虚拟机,可以通过eclipse启动,也可以通过命令启动: 1.通过android list avd命令查看已创建的虚拟机: 2.通过命令emulator @name启动虚拟机: 三.内存采集 1.通过dumpsys来取值,可以看到所有进程的内存信息,命令如下: adb shell dumpsys meminfo 2.查看详细的内存信息,命令如下: adb

Grafana + Influxdb Android性能监控部署

目录 简介 一.前提准备 二.安装 Grafana 三.安装 Influxdb 四.Grafana 添加 Influxdb 数据源 五.Shell 脚本写入数据到 Influxdb 简介 一.前提准备 本实例在 CentOS 7 虚拟机环境下实践,并通过 docker 进行 grafana+ influxdb 的安装 确保虚拟机能识别到 Android 设备(若不能识别,查看 VMware USB Arbitration Service 虚拟机USB相关服务是否开启) 已配置 AndroidSD

Google 发布 Android 性能优化典范

2015年伊始,Google发布了关于Android性能优化典范的专题, 一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个 方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计

MongoDB运行状态、性能监控,分析

转自http://tech.lezi.com/archives/290 MongoDB运行状态.性能监控,分析 Posted by neilxp on 十月 26, 2011Leave a comment (2)Go to comments 这篇文章的目的是让你知道怎么了解你正在运行的Mongdb是否健康. mongostat详解 mongostat是mongdb自带的状态检测工具,在命令行下使用.它会间隔固定时间获取mongodb的当前运行状态,并输出.如果你发现数据库突然变慢或者有其他问题的

[Android Pro] Android性能优化典范第一季

reference to : http://www.cnblogs.com/hanyonglu/p/4244035.html#undefined 2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议. 主要从三个方面展开,Android的渲染机制,内存与GC,电量优化.下

Android性能优化典范(一)

2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计师的

Android 性能优化探究

使用ViewStub动态加载布局,避免一些不经常的视图长期握住引用: ViewStub的一些特点: 1. ViewStub只能Inflate一次,之后ViewStub对象被置空:某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了. 2. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中. 基于以上的特点,那么可以考虑使用ViewStub的情况有: 1. 在程序的运行期间,某个布局在Inf

Android性能优化典范

2015年伊始,Google发布了关于Android性能优化典范的专题, 一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个 方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计

android 性能分析案例

本章以实际案例分析在android开发中,性能方面的优化和处理.设计到知识点有弱引用,memory monitor,Allocation Tracker和leakcanary插件. 1.测试demo 下载bug项目:https://github.com/lzyzsd/MemoryBugs,请注意配合使用MemoryMonitor, AllocationTracker以及HeapDump,LeakCanary等工具来查找潜在的内存问题,并尝试解决. 2.测试工具介绍 (1)memory monit