使用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,试用~
使用ddmlib实现android 性能监控_第1张图片

核心代码实现

控制器

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

更多相关文章

  1. 性能优化学习资源
  2. Android性能检测--traceview工具各个参数的意思
  3. traceview进行Android性能测试
  4. Android 性能监控与分析方法
  5. android ui 布局性能优化
  6. 收藏Android下bitmap内存限制OUT OF MEMORY的方法
  7. Android 内存管理 &Memory Leak & OOM 分析
  8. [置顶] Android防止内存溢出浅析
  9. 且谈Android内存溢出

随机推荐

  1. Android 读取U盘或SD卡中的所有.txt文件
  2. Android Studio——理解Intent和Intent F
  3. Android 开发中遇到的 bug(5)
  4. android实现标题栏、状态栏图标文字颜色
  5. Android Studio--报错日记
  6. [置顶] 我的Android进阶之旅------>Andro
  7. Android DEX 方法超过64K限制
  8. android--创建快捷方式和判断是否已经创
  9. Android ApiDemos示例解析(137):Views->L
  10. 高煥堂的四本Android开发新書(簡體完整版