码迷,mamicode.com
首页 > 移动开发 > 详细

使用ddmlib实现android 性能监控

时间:2015-08-06 16:58:14      阅读:944      评论:0      收藏:0      [点我收藏+]

标签:

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

}   

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

使用ddmlib实现android 性能监控

标签:

原文地址:http://blog.csdn.net/kittyboy0001/article/details/47317855

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!