安卓编程技巧总结(7) 性能检测代码分析


安卓性能分析的工具有很多,其中,较好用的工具,
如:AndroidStudio自带的Android Monitor(内存、网络、cpu、gpu)、腾讯的GT、第三方测试平台等。
如要使用第三方工具进行检测,请自行查询资料。

当然,也可以自己写代码进行测试。
本章,只是把各类性能检测分析的核心代码放出。
下面的代码经过测试,与Android Monitor、GT输出的结果一致:

  1. 内存
/**
 * 类:MemoryUtils
 * 作者: qxc
 * 日期:2017/12/15.
 */
public class MemoryUtils {
    private Context context;
    private Sampler sampler;
    private int timer;
    private String pageName;
    private String methodName;

    //数据结构:
    public static MemoryUtils memoryUtils;

    public static MemoryUtils getInstance() {
        if (memoryUtils == null) {
            synchronized (MemoryUtils.class) {
                if (memoryUtils == null) {
                    memoryUtils = new MemoryUtils();
                }
            }
        }
        return memoryUtils;
    }

    public void statMemory(Context context, int timer) {
        try {
            this.context = context;
            this.timer = timer;
            //按照设置timer开始监听内存变化
            if(sampler == null){
                sampler = new Sampler();
            }
            sampler.start();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    /**
     * 设置页面名称、方法名称
     * @param pageName 页面名称
     * @param methodName 方法名称
     */
    public void setPageInfo(String pageName, String methodName){
        this.pageName = pageName;
        this.methodName = methodName;
    }

    class Sampler implements Runnable {
        private ScheduledExecutorService scheduler;
        private Sampler() {
            scheduler = Executors.newSingleThreadScheduledExecutor();
        }

        public void start() {
            scheduler.scheduleWithFixedDelay(this, 0L, timer, TimeUnit.MILLISECONDS);
        }

        @Override
        public void run() {
            try {
                double memory = getMemory();
                Log.i(PerformanceConst.Tag, "@@ Sampler 记录内存");
                PageAnalysisStore.getInstance().storePageMemory(memory);
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }

        //获取内存
        private double getMemory() {
            double totalMemory = Runtime.getRuntime().totalMemory() * 1.0 / (1024 * 1024);
            double freeMemory = Runtime.getRuntime().freeMemory() * 1.0 / (1024 * 1024);
            return totalMemory - freeMemory;
        }
    }
}

调用:

//开启Memory监控
MemoryUtils.getInstance().statMemory(context, timer);
  1. CPU
/**
 * 类:CpuUtils
 * 作者: qxc
 * 日期:2017/12/15.
 */
public class CpuUtils {
    private Context context;
    private String pageName;
    private String methodName;
    private Sampler sampler;
    private int timer;

    //数据结构:
    public static CpuUtils cpuUtils;
    public static CpuUtils getInstance(){
        if(cpuUtils==null){
            synchronized (CpuUtils.class) {
                if(cpuUtils==null) {
                    cpuUtils = new CpuUtils();
                }
            }
        }
        return cpuUtils;
    }

    public void statCpu(Context context, int timer){
        try {
            this.context = context;
            this.timer = timer;
            //按照设置timer开始监听cpu变化
            if(sampler == null){
                sampler = new Sampler();
            }
            sampler.start();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    class Sampler implements Runnable {
        private ScheduledExecutorService scheduler;
        private Long lastCpuTime;
        private Long lastAppCpuTime;
        private RandomAccessFile procStatFile;
        private RandomAccessFile appStatFile;
        private DecimalFormat df2 = new DecimalFormat("0.00");

        private Sampler() {
            scheduler = Executors.newSingleThreadScheduledExecutor();
        }

        public void start() {
            scheduler.scheduleWithFixedDelay(this, 0L, timer, TimeUnit.MILLISECONDS);
        }

        @Override
        public void run() {
            try {
                double cpu = sampleCPU();
                Log.i(PerformanceConst.Tag, "@@ Sampler 记录CPU");
                PageAnalysisStore.getInstance().storePageCpu(cpu);
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }

        private double sampleCPU() {
            long cpuTime;
            long appTime;
            double sampleValue = 0.0D;
            try {
                if (procStatFile == null || appStatFile == null) {
                    procStatFile = new RandomAccessFile("/proc/stat", "r");
                    appStatFile = new RandomAccessFile("/proc/" + android.os.Process.myPid() + "/stat", "r");
                } else {
                    procStatFile.seek(0L);
                    appStatFile.seek(0L);
                }
                String procStatString = procStatFile.readLine();
                String appStatString = appStatFile.readLine();
                String procStats[] = procStatString.split(" ");
                String appStats[] = appStatString.split(" ");
                cpuTime = Long.parseLong(procStats[2]) + Long.parseLong(procStats[3])
                        + Long.parseLong(procStats[4]) + Long.parseLong(procStats[5])
                        + Long.parseLong(procStats[6]) + Long.parseLong(procStats[7])
                        + Long.parseLong(procStats[8]);
                appTime = Long.parseLong(appStats[13]) + Long.parseLong(appStats[14]);
                if (lastCpuTime == null && lastAppCpuTime == null) {
                    lastCpuTime = cpuTime;
                    lastAppCpuTime = appTime;
                    return sampleValue;
                }
                sampleValue = ((double) (appTime - lastAppCpuTime) / (double) (cpuTime - lastCpuTime)) * 100D;
                lastCpuTime = cpuTime;
                lastAppCpuTime = appTime;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return sampleValue;
        }
    }
}

调用

//开启CPU监测
CpuUtils.getInstance().statCpu(context, timer);
  1. Network
我们主要拦截的是URL请求数、重复请求数、超时请求数、请求时长、是否走缓存等等,用于分析网络执行情况。这个没什么技术点,请自行去做!!!
我们用的xUtils,自己做的拦截(不建议),后期,我们会更换到OkHttp或Retrofit;
如果网络库是OKHttp,自带了拦截,可根据需要自行统计,请自行查询资料;

/**
 * 类:NetworkUtils
 * 作者: qxc
 * 日期:2017/12/15.
 */
public class NetworkUtils {
    public static NetworkUtils networkUtils;
    public static NetworkUtils getInstance() {
        if (networkUtils == null) {
            synchronized (NetworkUtils.class) {
                if (networkUtils == null) {
                    networkUtils = new NetworkUtils();
                }
            }
        }
        return networkUtils;
    }

    public void statNetwork(String url, Long startTime,Long endTime,int responseCode) {
        try {
            Log.i(PerformanceConst.Tag, "@@ Sampler 记录网络");
            PageAnalysisStore.getInstance().storePageNetwork(url,startTime,endTime,responseCode);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

4.Fps(帧率,页面卡顿情况)

/**
 * 类:FpsUtils
 * 作者: qxc
 * 日期:2017/12/15.
 */

public class FpsUtils{
    //数据结构:
    public static FpsUtils fpsUtils;
    public static FpsUtils getInstance(){
        if(fpsUtils==null){
            synchronized (FpsUtils.class){
                if(fpsUtils==null) {
                    fpsUtils = new FpsUtils();
                }
            }
        }
        return fpsUtils;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public void statFPS(Context context, int timer){
        try {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                Choreographer.getInstance().postFrameCallback(new FPSFrameCallback(timer));
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
/**
 * 类:FPSFrameCallback
 * 作者: qxc
 * 日期:2018/1/2.
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class FPSFrameCallback implements Choreographer.FrameCallback{
    private static final String TAG = "FPS_TEST";
    private ExecutorService executorService;
    private long lastTime = 0;
    private int timer = 1000;
    private float minFps = 60;

    public FPSFrameCallback(int timer) {
        this.timer = timer;
        lastTime = System.nanoTime();//获得当前时刻(纳秒)
        executorService = Executors.newCachedThreadPool();//初始化缓存线程池
        handler.postDelayed(runnable, timer);//计时开始
    }

    @Override
    public void doFrame(long l) {
        try {
            if (lastTime > 0) {
                float time = l - lastTime;//计算两帧之间的时间差
                float frame = 1000000000 / time;//计算界面实时帧数
                if (frame < minFps) {
                    minFps = frame;//获得1s内的最小帧
                }
                lastTime = l;
            }
            //注册下一帧回调
            Choreographer.getInstance().postFrameCallback(this);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        //存储FPS
                        Log.i(TAG, "存储FPS: "+ minFps);
                        PageAnalysisStore.getInstance().storePageFps(minFps);
                    }
                });

                handler.postDelayed(this, timer);
                minFps = 60;//设置为初始值
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
    };
}

调用:

//开启FPS监测
FpsUtils.getInstance().statFPS(context, timer);
  1. 电量变化(测试时,不能插USB充电,否则无法测试,腾讯GT也是如此)
/**
 * 描述:
 * 作者:小辉
 * 时间:2017/12/220931
 */
public class BatteryService extends Service {
    BatteryReceiver batteryReceiver;
    IntentFilter intentFilter;
    private int startBattery = 0;
    private String startTime = "";
    private String endTime = "";
    private int endBattery = 0;
    private boolean isCharge = true;

    /**
     * 广播接受者
     */
    class BatteryReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //判断它是否是为电量变化的Broadcast Action
            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
                //获取当前电量
                int level = intent.getIntExtra("level", 0);
                //电量的总刻度
                // int scale = intent.getIntExtra("scale", 100);
                //电池状态,返回是一个数字
                // BatteryManager.BATTERY_STATUS_NOT_CHARGING 未充电
                int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
//                Toast.makeText(BatteryService.this, "电量状态", Toast.LENGTH_SHORT).show();
//                Log.e("杀死服务", "电量状态");
                endTime = getTime();
                if (!isCharge) {//不统计电量
                    Performance.getInstance().onBattery(0, 0, startTime, endTime);
//                    Toast.makeText(BatteryService.this, "不统计电量" + status, Toast.LENGTH_SHORT).show();
                } else {
                    if (status == 4) {//APP未充电状态
                        if (startBattery == 0) {
                            startBattery = level;
                        }
                        endBattery = level;
                        Performance.getInstance().onBattery(startBattery, endBattery, startTime, endTime);
//                        Toast.makeText(BatteryService.this, "统计电量" + "统计电量", Toast.LENGTH_SHORT).show();
                    } else {
                        isCharge = false;
                        Performance.getInstance().onBattery(0, 0, startTime, endTime);
//                        Toast.makeText(BatteryService.this, "APP充电状态不统计电量", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startBattery = 0;
        //注册广播接受者java代码
        intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        //创建广播接受者对象
        batteryReceiver = new BatteryReceiver();
        if (startTime.equals("")) {
            startTime = getTime();
        }
        //注册receiver
        registerReceiver(batteryReceiver, intentFilter);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        if (batteryReceiver != null) {
            this.unregisterReceiver(batteryReceiver);
        }
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private String getTime() {
        long time = System.currentTimeMillis();
        return String.valueOf(time);
    }
}

第一篇: 安卓编程技巧总结(1) 资源与UI布局处理

https://www.jianshu.com/p/ff97b15d5c9d

第二篇: 安卓编程技巧总结(2) 基础组件开发

https://www.jianshu.com/p/b05752377887

第三篇:安卓编程技巧总结(3) 进程与线程处理

https://www.jianshu.com/p/7d05c8a368bd

第四篇:安卓编程技巧总结(4) 数据文件处理

https://www.jianshu.com/p/0515df3b697d

第五篇:安卓编程技巧总结(5) 图片处理

https://www.jianshu.com/p/76690b2ba310

第六篇:安卓编程技巧总结(6) APP安全分析

https://www.jianshu.com/p/4347ff392122

第七篇:安卓编程技巧总结(7) 性能检测代码分析

https://www.jianshu.com/p/687f3c641408

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335