android开发 实现动态获得app的cpu占有率并导出文件的两种方法。

android开发 实现动态获得app的cpu占有率并导出文件的两种方法。

最近在做学校实验室的项目的时候,师兄要求我对app的性能进行评估,主要是从电量、cpu占有率、python模型的响应时间三者进行统计分析,电量使用广播可以进行统计、python模型的运行速度用时间函数就可以计算,关于cpu占有率~interesting!这是值得研究一下的。

阅读指南
1、想法一暂时无法解决问题。
2、想法二可以实现在java中动态获得app的cpu占有率,便于大家实现显示在前端界面的需求,但是,这种获得占有率的方法速度较慢,大概1s获得3~4个cpu占有率,所以如果对cpu占有率的采样率具有很高的要求的话,不建议使用第二种方法。
3、想法三可以满足你大幅度采样cpu占有率的需求,通过调整top指令中 -d [time] 参数的大小你可以控制cpu采样的速度,但是这个方法不方便展示在前端界面中。

想法一:Android平台是否为开发者提供cpu占有率接口?

我找了很久,也没找到有对应的接口,如果有的话,欢迎大家在评论区中指正,谢谢啦!!

想法二:通过调用Linux命令获得cpu占有率。

我们都知道,android OS的底层其实也是基于Linux,所以在Linux上的一些命令也能够用在android系统中,但是我们先用Linux来试一下(大家没有Linux系统也没关系,我们后面会在java代码中调用指令可以达到相同的效果)。

  1. 在Linux系统的命令行界面中输入top指令,可以得到下面图片中的效果。
top

android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第1张图片

图中,我们运行了top命令后,会发现出现一个不断刷新的表,其实这就是此时linux系统中正在运行的进程的状况,可以看见我们想要获得的参数cpu占有率就在倒数第三列,同时我们也可以发现其中的每一个进程都对应一个pid,它就相当于一个身份证,唯一标识着系统中运行的一个进程

  1. 从上面运行的top命令,我们成功地获得了在linux中进程的运行情况,但是展现的列表太长了,有很多进程甚至还上不了榜(因为这个表是根据优先cpu占有率进行排行,太低的cpu占有率上不了榜),如何通过top命令去筛选出自己想监控的进程呢?通过pid就可以解决了!!(这里以一个firefox浏览器进程为例子)。

在Linux中,你可以使用pstree指令获得系统进程树(方法不唯一,你可以通过这个来找到你关注的应用程序的pid)。

pstree -p
或者
pstree -p | grep [进程名]

android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第2张图片
我们可以看到firefox的进程id为2328,于是我们可以运行,结果如下图所示。

top -p [pid]

android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第3张图片
我们再加上-d参数控制刷新时间(s), 如top -p 2368 -d 0.5,命令行会0.5秒刷新一次输出

top -p [pid] -d [time]

在这里插入图片描述
于是我们可以得出结论,我们可以通过top命令获得应用程序的占有率!!!

  1. 回到android中,我们用java实现在命令行中获取cpu占有率,动态显示在界面上和保存在文件中。

先贴代码~

package com.example.bluechatapp.Performance;
import android.content.Context;
import android.os.Environment;
import com.example.bluechatapp.Static.Utils.LogUtils;
import com.example.bluechatapp.MainActivity.MainActivity;
import com.example.bluechatapp.Static.Utils.ToastUtil;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class CPURunnable implements Runnable{
    private PerformanceRecorder recorder ;
    private Context context ;
    private File performanceFile ;
    public CPURunnable(PerformanceRecorder recorder,Context context) throws IOException {
        this.recorder = recorder;
        this.context = context ;
        try{
        	// 初始化保存目录
            initSaveDirectory() ;
        }catch (Exception e){
            // 保存错误日志
            ToastUtil.showShortMessage(context,"错误报告正在生成");
            LogUtils.recordLog(context,"CpuRunnable_cn",".txt",e.getMessage());
        }
    }
    /**
     * 初始化保存目录
     */
    private void initSaveDirectory() throws IOException {
        // 在app目录中初始化保存目录,并生成对应文件
        File rootDir = new File(String.valueOf(Environment.getExternalStorageDirectory()),"EarMotion") ;
        if (!rootDir.exists()){
            rootDir.mkdirs() ;
        }
        File performanceDir = new File(rootDir.getPath(),"Performance") ;
        if (!performanceDir.exists()){
            performanceDir.mkdirs() ;
        }
        performanceDir = new File(performanceDir.getPath(),"CPU") ;
        if (!performanceDir.exists()){
            performanceDir.mkdirs() ;
        }
        int length = performanceDir.listFiles().length + 1 ;
        performanceFile = new File(performanceDir.getPath(),(length+".txt"));
        if (!performanceFile.exists()){
            performanceFile.createNewFile() ;
        }
        OutputStream stream = new FileOutputStream(performanceFile,false) ;
        stream.write("cpu rate:\ntime(s)\t\trate\n".getBytes() );
        stream.close() ;
    }
    @Override
    public void run() {
        long begin = System.currentTimeMillis() ;
        try {
            OutputStream stream = new FileOutputStream(performanceFile,true);
            while(recorder.isMonitoring()){
                try {
                    // 主要注意这里,从recorder中获得cpu占有率
                    float rate = recorder.getCpuRateFromTerminal() ;
                    stream.write((String.valueOf((System.currentTimeMillis()-begin)/1000)+"\t\t"+rate+"%\n").getBytes());
                    MainActivity.setCpuTextView("CPU占比:"+rate+"%");
                } catch (Exception e) {
                    ToastUtil.showShortMessage(context,"错误报告正在生成");
                    LogUtils.recordLog(context,"CpuRunnable",".txt",e.getMessage());
                }
            }
            stream.close() ;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Process;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static com.example.bluechatapp.MusicActivity.MusicPlayerHelper.TAG;
public class PerformanceRecorder {
    /**
     * 上下文
     */
    private Activity context ;
    /**
     * 终端执行的指令,注意这里!!!!!
     */
    private static String COMMAND = "top -n 1 -p " ;
    /**
     * 单例模式
     */
    private static volatile PerformanceRecorder recorder = null ;
    /**
     * 该应用程序的唯一标识
     */
    private static int pid ;
    /**
     * 匹配浮点数
     */
    private static String NUMBER_REGEX = "^[0-9]+(.[0-9]+)?$";
   
    /**
     * 构造函数,初始化pid
     * @param context
     */
    private PerformanceRecorder(Activity context) {
        this.context = context;
        // 获得该应用程序的进程pid
        pid = Process.myPid() ;
        // 指令合成
        COMMAND = COMMAND + pid ;
    }
    /**
     * 单例模式工厂,双重锁定
     * @param context
     * @return
     */
    public static PerformanceRecorder newInstance(Activity context) {
        if (recorder==null){
            synchronized (PerformanceRecorder.class){
                if (recorder==null)
                    recorder = new PerformanceRecorder(context) ;
            }
        }
        return recorder ;
    }

    /**
     * 从终端接收此时的平均cpu占比,如果返回-1,则是错误的
     * @return
     * @throws IOException
     */
    public float getCpuRateFromTerminal() throws Exception {
            // Linux运行指令!!注意这里!!
            java.lang.Process runtime = Runtime.getRuntime().exec(COMMAND) ;
            // 获得终端的输入流!!!
            BufferedReader terminalBufferedStream = new BufferedReader(new InputStreamReader(runtime.getInputStream())) ;
            String line = null ;
            String lastLine = null ;
            String dataLine = null ;
            List<String> floatNumber = new ArrayList<>() ;
            // 这里注意从命令行获得的输入流有多行,我们取最后一行,这里可以结合具体的输入流来看,会更好理解
            while((line=terminalBufferedStream.readLine())!=null&&line!=" "){
                dataLine = lastLine ;
                lastLine = line ;
            }
            dataLine = dataLine.trim() ;
            String []array = dataLine.split(" ") ;
            // 正则表达式,提取其中的浮点数
            for (String i:array){
                if (i.matches(NUMBER_REGEX)){
                    floatNumber.add(i) ;
                }
            }
            // 获得系统的cpu核心,用于求平均占比
            int cpuCores = Runtime.getRuntime().availableProcessors() ;
            if (floatNumber.size()==4&&floatNumber.get(0).equals(String.valueOf(pid))){
            // 第三个浮点数就是cpu占比
                Log.d(TAG, "cpu rate: "+floatNumber.get(2)+"/"+cpuCores+"%");
                Log.d(TAG, "getCpuRateFromTerminal: "+line);
                terminalBufferedStream.close();
                runtime.destroy();
                return Float.valueOf(floatNumber.get(2)) / cpuCores ;
            }
            terminalBufferedStream.close();
            runtime.destroy();
        return -1 ;
    }

 
    /**
     * 启动cpu监视线程
     */
    public void startCpuMonitor() throws IOException {
        ExecutorService cpuPool = Executors.newSingleThreadExecutor();
        cpuPool.submit(new CPURunnable(this,context)) ;
    }
}

调用getCpuxxx函数,把返回值不断写到textView中就能实现以下效果,他是不断在变化的
实现效果:
android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第4张图片
android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第5张图片

以上的代码,大家只要清楚关键,就是通过java的RunTime类运行Linux指令,获得终端的输入流进行分析即可,具体可以根据自己的具体情况对代码进行调整

想法三:通过adb工具从手机内核中到处数据文件。

经常开发安卓的同学都应该知道有adb这种神器工具,它能够进入到手机内核中像在Linux一样观察各种进程的运行情况,那么在项目的最后我也是采用这种方法来完成师兄的需求。

  1. adb进入内核。
adb shell

android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第6张图片
进入手机内核之后,我们可以像在Linux中一样运行各种终端指令

  1. 运行top指令,如top -d 0.05 | grep com.example.bluechatapp > /sdcard/cpuTime.log。
top -d [time(s)] | grep [包名] > /sdcard/[保存文件名]

android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第7张图片
指令运行后,会一直阻塞在这里,此时系统正在帮你统计程序的信息,我们通过ctrl + C可以结束并保存文件

  1. 退出adb,运行adb指令推出,如adb pull /sdcard/cpuData.log,如果文件保存成功将会显示提示。
adb pull /sdcard/[文件名]

在这里插入图片描述

  1. 运行ls指令,观察当前目录下文件,可以发现我们保存的文件。
    android开发 实现动态获得app的cpu占有率并导出文件的两种方法。_第8张图片
  2. 用excel打开,注意筛选pid,采用空格进行分格,可以获得我们进程的数据,图中J列就是cpu占有率(我的手机是8核,所以会出现高于100的情况,除以8就行)。

至此,全剧终,完结撒花

总结

可见我们实现了两种方法,来提取cpu占有率,其实获得内存占比等都是一样的,谢谢大家的阅读!您的观看是我前进的动力!欢迎大家在评论区中讨论,指出我的不足!

谢谢大家!!

你可能感兴趣的:(Android,Java,java,android,linux)