Android 性能测试小工具 (cpu,内存,电量,流量数据浮窗显示,信息导入数据库、sd卡)

1 篇文章 0 订阅

【mie haha的博客】转载请注明出处(万分感谢!):
https://blog.csdn.net/qq_40315080/article/details/98610748

写一个测试各个app/手机性能的小工具app。

功能:

  1. 打开app得到手机中已有的应用列表,点击可以跳转进入。
  2. 上方标题栏上有选择菜单,可以选择打开,刷新,关闭浮窗,浮窗可以拖动。
  3. 打开浮窗后,浮窗将显示顶层(当前所在)app的cpu占用、内存、流量,手机总cpu占用、电量数据,每几秒实时更新数据。
  4. 一个开关按钮,点击控制数据导入数据库和SD卡或停止导入。(可选)
  5. 一个选择按钮,点击控制监测限定某个app或监测顶层app的数据。(可选)
  6. 点击浮窗返回该小工具app界面。(可选)

(标记可选的与主要功能无关,只是使使用稍微更方便,可以选择性添加)

主要功能代码:

首先小工具app打开是手机中app列表,需要先得到app列表。

1.获取app列表:

每个app有它对应的名称,包名,进程号(pid),uid。
【pid:每个app运行时都会开启一个进程来实际运行,用pid来标识这个进程,有的app可能会开启多个进程。
在利用android自带的方法获得数据时都是通过pid获取数据,对于多进程app需要把所有进程的数据相加。】
【uid:像一个身份证,唯一地标识某个app。】
【一般包名和名称相比pid和uid更容易得到。】

将app的信息封装在一个类中方便使用。再用该类创建一个列表来保存所有的app信息。最后将这个列表显示在界面上即可得到app列表。

(1)封装app信息:

public class AppInfo  {

    public Drawable mappimage = null;
    public String mappname = "";
    public String mpackagename = "";

    //构造方法
    public AppInfo(Drawable appimage,String appname,String packagename){
        this.mappimage=appimage;
        this.mappname=appname;
        this.mpackagename=packagename;
    }

    public String getMappname(){return this.mappname;}
    public String getMpackagename(){return this.mpackagename;}

}

(2)创建列表,把获得的app信息加入列表(以下代码写在MainActivity中):


//app信息列表
public static ArrayList<AppInfo> applist = new ArrayList<AppInfo>();

//获取app列表
public void getAppProcessName(Context context) {
 
    final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

    final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);

    for (int i = 0; i < apps.size(); i++) {
        Drawable imageicon = apps.get(i).activityInfo.loadIcon(packageManager); //图标
        String appname = apps.get(i).activityInfo.applicationInfo.loadLabel(packageManager).toString(); //名称
        String packagname = apps.get(i).activityInfo.packageName; //包名
        AppInfo appinfo = new AppInfo(imageicon, appname, packagname);  //appinfo对象
        applist.add(appinfo);  //加入队列
        Log.i("NAME", "getAppProcessName: " + appname);  //打印日志输出得到的app名字检查一下
    }
}

app信息列表用LisView来显示,每一项包括app图标,app名称,包名。这需要给ListView添加适配器,用适配器来指定每一项的形式和内容。

(3)适配器:

//适配器类
public class MyAdapter extends ArrayAdapter<AppInfo> {

    private Context mContext;
    private int mresource;
    private List<AppInfo> list;
    private Activity selectmain;
    private Intent jumpintent;

    public MyAdapter(Context context,int resource,List<AppInfo> data,Activity selectmain,Intent jumpintent){
        super(context,resource,data);
        this.mContext=context;
        this.mresource=resource;
        this.list=data;
        this.selectmain=selectmain;
        this.jumpintent=jumpintent;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        ViewHolder holder=null;
        if(convertView==null){
            holder=new ViewHolder();
            convertView= LayoutInflater.from(mContext).inflate(mresource,null);
            holder.image=(ImageView) convertView.findViewById(R.id.appimage);
            holder.title=(TextView) convertView.findViewById(R.id.appname);
            holder.text=(TextView)convertView.findViewById(R.id.packagname);
           // holder.button=(Button)convertView.findViewById(R.id.appbutton);
            convertView.setTag(holder);
        }else{
            holder=(ViewHolder)convertView.getTag();
        }

        holder.image.setImageDrawable(getItem(position).mappimage);
        holder.title.setText(getItem(position).mappname);
        final String m_appname = getItem(position).mappname;
        holder.text.setText(getItem(position).mpackagename);
        holder.text.setSelected(true);//包名太长,走马灯显示
        final String m_packagename = getItem(position).mpackagename;

    public class ViewHolder{
        ImageView image;
        TextView title;
        TextView text;
       // Button button;
    }


    //跳转进入应用
    private void launch(String packagename) {

        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){
            selectmain.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
        }

        Intent intent = selectmain.getPackageManager().getLaunchIntentForPackage(packagename);
        //非空跳转
        if (intent != null) {
            selectmain.startActivity(intent);
        } else {
            // 没有安装要跳转的应用
            Toast.makeText(selectmain.getApplicationContext(), "没有找到app", Toast.LENGTH_LONG).show();
        }
    }


}

并给ListView列表添加适配器:

ListView listView=(ListView)findViewById(R.id.applist);  //找到ListView
MyAdapter arrayAdapter=new MyAdapter(this,R.layout.applistform,applist,this,jumpintent);
listView.setAdapter(arrayAdapter);  //添加适配器

(4)最后,实现点击跳转,需要给ListView添加点击监听器:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
     Log.e("listview","点击了");
     AppInfo oneapp = applist.get(position);
     String name = oneapp.getMappname();
     String pname = oneapp.getMpackagename();
     Toast.makeText(MainActivity.this,"已选择: "+name,Toast.LENGTH_SHORT).show();

     //mode=0原来的跳转
     if(MainActivity.mode==0) {
         launch(pname);
         Log.e("switch","选择:跳转");
     }
     else {
         //不跳转,记录当前点击的app
         MainActivity.testpackagename = pname;
         Log.e("switch","选择:不跳转");
     }
 }
});

2. 打开,关闭,刷新浮窗(可拖动):

(1)首先把打开浮窗,浮窗显示信息,浮窗跟随手移动都写在一个方法中,这样在界面上直接调用这个方法就好。

在浮窗上显示信息其实就是在浮窗上显示一个文本框TextView,可被拖动就是给TextView添加触摸监听器,每次得到手触摸的位置坐标并按照该坐标更新TextView的显示坐标即可。

//打开浮窗
private void openwindow(Context context){

   lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT | WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
   lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
   lp.gravity = Gravity.LEFT | Gravity.TOP;  //显示在屏幕左上角

   //显示位置与指定位置的相对位置差
   lp.x = 0;
   lp.y = 0;
   //悬浮窗的宽高(直接设置为WRAP_CONTENT恰好与显示内容一样大比较方便,也可以自己试试设置为合理的确定大小如500,400)
   lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
   lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
   //lp.width = 500;
   //lp.height = 400;

   //设置浮窗背景色为透明
   lp.format = PixelFormat.TRANSLUCENT;

   //移除窗口
   if (mtextView != null) {
       windowManager.removeView(mtextView);
   }
   
   //创建要在浮窗上显示的文本框,来显示信息
   mtextView = new TextView(getApplicationContext());
   mtextView.setTextColor(Color.rgb(255, 251, 240));
   mtextView.setBackgroundColor(Color.parseColor("#BFA9A9A9")); //设置textview颜色为半透明灰色,前两位‘BF’表示透明度
   windowManager.addView(mtextView, lp); //把textview添加到浮窗上

   //显示位置与指定位置的相对位置差
   imagelp.x = lp.x+lp.width;
   imagelp.y = lp.y+lp.height;
   //悬浮窗的宽高
   imagelp.width = WindowManager.LayoutParams.WRAP_CONTENT;
   imagelp.height = WindowManager.LayoutParams.WRAP_CONTENT;
   windowManager.addView(imageback,imagelp);

   /* 这里是用来不断生成实时数据的,下面会具体说到,所以这里先注释掉了。完整代码中是有这两句的!!!!
   CPUThread cpuThread = new CPUThread(mtextView,this,mActivityManager);
   cpuThread.start();
   */

   //textview触摸监听,实现浮窗可被拖动
   mtextView.setOnTouchListener(new View.OnTouchListener() {
       private float lastX, lastY;
       private float nowX, nowY;
       private float tranX, tranY;

       @Override
       public boolean onTouch(View v, MotionEvent event) {
           boolean ret = false;
           switch (event.getAction()) {
               //按下
               case MotionEvent.ACTION_DOWN:
                   //上次位置
                   lastX = event.getRawX();
                   lastY = event.getRawY();
                   ret = true;
                   break;
               //拖动
               case MotionEvent.ACTION_MOVE:
                   //当前目标位置
                   nowX = event.getRawX();
                   nowY = event.getRawY();
                   tranX = nowX - lastX;
                   tranY = nowY - lastY;
                   //移动
                   lp.x += tranX;
                   lp.y += tranY;
                   //更新位置
                   windowManager.updateViewLayout(mtextView, lp);
                   //记录当前坐标作为下一次计算的上一次移动的位置坐标
                   lastX = nowX;
                   lastY = nowY;
                   break;
               //手抬起不操作
               case MotionEvent.ACTION_UP:
                   break;
           }
           return ret;
       }
   });

}

(2)完成了打开浮窗的方法,接下来调用即可。但不是所有手机都可以允许app打开浮窗,这需要提前判断是否有打开浮窗的权限,如果没有权限,需要跳转到权限界面打开权限,再打开浮窗;如果有打开权限直接打开。

手机安装的sdk版本不同,有不同的默认权限设置。sdk高于23的手机一般默认没有打开浮窗权限,需要跳转权限界面打开。低于23的手机一般有权限,可直接打开。

 //检查sdk版本
if (Build.VERSION.SDK_INT >= 23) {
        //浮窗权限判断
        if (Settings.canDrawOverlays(MainActivity.this)) {
            //打开浮窗
            openwindow(MainActivity.this);
        } else {
            //若没有权限,提示获取.
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
            intent.setPackage(getPackageName());
            Toast.makeText(MainActivity.this, "需要取得权限以使用悬浮窗", Toast.LENGTH_SHORT).show();
            startService(intent);
        }
    } else {
        //低级sdk打开浮窗
        openwindow(MainActivity.this);
    }
    Toast.makeText(this, "打开浮窗", Toast.LENGTH_SHORT).show();

浮窗已经有了雏形,但是缺少数据信息来显示。
因为要检测顶层app(即当前app)的数据,需要得到顶层app。

3. 获得顶层app:

因为不同信息获取要用到的app信息不一样,取得顶层app时要取得app包名,名称,pid。uid。

包名:

//得到顶层应用包名
private String getForegroundApp() {

      boolean isInit = true;
      UsageStatsManager usageStatsManager = (UsageStatsManager) mainactivity.getApplication().getSystemService(Context.USAGE_STATS_SERVICE);
      long ts = System.currentTimeMillis();
      List<UsageStats> queryUsageStats =
              usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 0, ts);
      UsageEvents usageEvents = usageStatsManager.queryEvents(isInit ? 0 : ts-5000, ts);
      if (usageEvents == null) {
          return null;
      }

      UsageEvents.Event event = new UsageEvents.Event();
      UsageEvents.Event lastEvent = null;
      while (usageEvents.getNextEvent(event)) {
          // if from notification bar, class name will be null
          if (event.getPackageName() == null || event.getClassName() == null) {
              continue;
          }

          if (lastEvent == null || lastEvent.getTimeStamp() < event.getTimeStamp()) {
              lastEvent = event;
          }
      }

      if (lastEvent == null) {
          return null;
      }
      return lastEvent.getPackageName();
  }

名称:

/得到顶层应用名
public static String getProgramNameByPackageName(Context context, String packageName) {
      PackageManager pm = context.getPackageManager();
      String name = null;
      try {
          name = pm.getApplicationLabel(
                  pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString();
      } catch (PackageManager.NameNotFoundException e) {
          e.printStackTrace();
      }
      return name;
  }

pid:

//得到顶层pid
private int forespid(String baoming){
    int getforepid=0;
    List<ActivityManager.RunningAppProcessInfo> runningAppsInfo = new ArrayList<ActivityManager.RunningAppProcessInfo>();
    PackageManager pm = mainactivity.getPackageManager();
    ActivityManager am = (ActivityManager) mainactivity.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(Integer.MAX_VALUE);
    for(ActivityManager.RunningServiceInfo service: runningServices) {

        String pkgName = service.process.split(":")[0];

        ActivityManager.RunningAppProcessInfo item = new ActivityManager.RunningAppProcessInfo();
        item.pkgList = new String[] { pkgName };
        item.pid = service.pid;
        int pid=item.pid;
        item.processName = service.process;
        String processname =item.processName;
        for(String apakagename : item.pkgList){


            Log.e("新方法找到pid","             当前遍历的包名:"+apakagename+"顶层包名:"+baoming);
            Log.e("新方法找到pid","             当前遍历的包名:"+processname+"顶层包名:"+baoming);
            if(baoming.equals(apakagename)) {
                Log.e("新方法找到pid", "!!!!!!!!!!!!!!!!!!!!!" + baoming+"pid:  "+pid);
                getforepid = pid;
            }
        }

    }
    return getforepid;
}

uid:

//包名获得uid
public int foreUID(String pn){
    PackageManager mPm = mainactivity.getPackageManager();
    int uuid = 0;
    try {
        ApplicationInfo applicationInfo = mPm.getApplicationInfo(pn, 0);
        uuid = applicationInfo.uid;
        Toast.makeText(mainactivity, "", Toast.LENGTH_SHORT).show();
    }catch (Exception e){
        e.printStackTrace();
    }
    Log.e("找uid:",uuid+"");
    return uuid;
}

4. 获得cpu总占用率:

获得总体cpu占用率比获取某个app的cpu占用率简单。手机的性能数据都保存在系统中,我们只需要输入命令来调取即可。先来看一下系统中怎么保存的cpu信息:

我们需要的数据信息全部保存在/proc文件下。数据根据系统自动更新。
连接手机,在命令行输入adb shell进入shell模式,再输入cat进行 /proc/stat文件 的查看。可以看到:
在这里插入图片描述
(图片来自https://www.jianshu.com/p/6bf564f7cdf0)

第一行是总cpu使用情况。一个手机可能有多个cpu,下面几行是多个cpu的各自使用情况。由cpu0—cpu3可知该连接的手机共有4个cpu处理内核。cpu数据的单位是jiffies,jiffies 是内核中的一个全局变量,用来记录系统启动以来产生的节拍数,根据不同的linux系统在1ms 到 10ms 之间。

用获取的信息计算使用率:

公式:totalCPUrate = (非空闲cpu时间2-非空闲cpu时间1)/(cpu总时间2-cpu总时间1)x100%

一般在较短时间内进行2次取样,计算。
取得的cpu数据是10元组,包括user、nice、system、idle、iowait、irq、softirq、stealstolen、guest、guest_nice。

代码:

//计算cpu总占用率
public static String getCPURateDesc_All(){
   String path = "/proc/stat";// 系统CPU信息文件
   long totalJiffies[]=new long[2];
   long totalIdle[]=new long[2];
   int firstCPUNum=0;//设置这个参数,这要是防止两次读取文件获知的CPU数量不同,导致不能计算。这里统一以第一次的CPU数量为基准
   FileReader fileReader = null;
   BufferedReader bufferedReader = null;
   Pattern pattern=Pattern.compile(" [0-9]+");
   for(int i=0;i<2;i++) {
       totalJiffies[i]=0;
       totalIdle[i]=0;
       try {
           fileReader = new FileReader(path);
           bufferedReader = new BufferedReader(fileReader, 8192);
           int currentCPUNum=0;
           String str;
           while ((str = bufferedReader.readLine()) != null&&(i==0||currentCPUNum<firstCPUNum)) {
               if (str.toLowerCase().startsWith("cpu")) {
                   currentCPUNum++;
                   int index = 0;
                   Matcher matcher = pattern.matcher(str);
                   while (matcher.find()) {
                       try {
                           long tempJiffies = Long.parseLong(matcher.group(0).trim());
                           totalJiffies[i] += tempJiffies;
                           if (index == 3) {//空闲时间为该行第4条栏目
                               totalIdle[i] += tempJiffies;
                           }
                           index++;
                       } catch (NumberFormatException e) {
                           e.printStackTrace();
                       }
                   }
               }
               if(i==0){
                   firstCPUNum=currentCPUNum;
                   try {//暂停50毫秒,等待系统更新信息。
                       Thread.sleep(50);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       } catch (IOException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       } finally {
           if (bufferedReader != null) {
               try {
                   bufferedReader.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }
   //计算
   double rate=-1;
   if (totalJiffies[0]>0&&totalJiffies[1]>0&&totalJiffies[0]!=totalJiffies[1]){
       rate=1.0*((totalJiffies[1]-totalIdle[1])-(totalJiffies[0]-totalIdle[0]))/(totalJiffies[1]-totalJiffies[0]);
   }

   Log.d("CpuUtils","zrx---- cpu_rate:"+rate);
   return String.format("cpu:%.2f %s",rate*100,"%");
}

5. 获得某个app的cpu占用率:

用获取到的顶层cpu信息,取相应的数据获得某app的cpu使用率。

2.中已经得到了顶层app的pid,用pid获取该app的cpu信息:

private double sampleCPU(int pid) {
    long cpuTime;
    long appTime;
    double sampleValue = 0.0D;
    try {
        if (procStatFile == null || appStatFile == null) {
            procStatFile = new RandomAccessFile("/proc/stat", "r");
            appStatFile = new RandomAccessFile("/proc/" + pid+ "/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;
        }
        Log.e("thiscpu","分子"+(double) (appTime - lastAppCpuTime)+"");
        Log.e("thiscpu","分母"+ (double) (cpuTime - lastCpuTime)+"");
        this.dbthiscpu = (double) (appTime - lastAppCpuTime);  //db显示当前进程cpu消耗
        this.dball = (double) (cpuTime - lastCpuTime);  //db显示整体cpu消耗
        sampleValue =  100D * ((double) (appTime - lastAppCpuTime) / (double) (cpuTime - lastCpuTime));
        lastCpuTime = cpuTime;
        lastAppCpuTime = appTime;
        Log.e("ppp",pid+"");
        Log.e("ppp","正常");
    } catch (Exception e) {
        Log.e("ppp",pid+"");
        Log.e("ppp","异常");
        e.printStackTrace();
    }
    return sampleValue;
}

最后规定下显示的小数形式。

DecimalFormat df = new DecimalFormat("0.000");
Double d = sampleCPU(forepid);  //forpid是顶层app的pid,由2.知可由包名得到
this.thiscpu = "cpu:"+df.format(d)+"%";

6. 获得某个app的内存:

由app的包名得到app占用的全部内存:

//包名得到内存占用
private double getMem(String pn) {
    double allmem = 0;
    String ppn = null;
    List<AppInfo> resule = new ArrayList<AppInfo>();
    ActivityManager am = (ActivityManager) mainactivity.getSystemService(Context.ACTIVITY_SERVICE);
    PackageManager pm = mainactivity.getPackageManager();
    AppUtils proutils = new AppUtils(mainactivity);
    List<AndroidAppProcess> listInfo = ProcessManager.getRunningAppProcesses();
    if(listInfo.isEmpty() || listInfo.size() == 0){

    }
    if(listInfo!=null){ }
    for (AndroidAppProcess info : listInfo) {
        ApplicationInfo app = proutils.getApplicationInfo(info.name);
        ppn = info.getPackageName();
        if(ppn.equals(pn)){
            // 计算应用所占内存大小
            int[] myMempid = new int[] { info.pid };
            Debug.MemoryInfo[] memoryInfo = am.getProcessMemoryInfo(myMempid);
            double memSize = memoryInfo[0].dalvikPrivateDirty / 1024.0;
            this.dbthismemory= allmem + memoryInfo[0].dalvikPrivateDirty;//数据库显示的当前进程内存占用原始数据
            int temp = (int) (memSize * 100);
            memSize = temp / 100.0;
            allmem = allmem+memSize;
        }

    }
    Log.e("hhh",allmem+"        "+ppn);
    return allmem;
}

7. 获得某个app的流量:

流量是接受量和发送量的总和。

由app的uid获得流量使用量:

//得到流量
public long FFlow(int uid) {
     long rx = TrafficStats.getUidRxBytes(uid);// 总接收量
     long tx = TrafficStats.getUidTxBytes(uid);// 总发送量
     Log.e("FFlow","包名:"+this.packagename+"uid:" +uid+" 接收:"+rx+" 发送:"+tx);
     this.dbflow = rx+tx+"";
     return (rx+tx)/1024;   //单位是MB
 }

(app的uid的获得方法在2.中)

8. 把数据信息显示到浮窗,并实时刷新:

要实时刷新,就是要不停重新获取数据。这就需要使用线程,把设置显示信息的语句放到线程中执行。安卓中不能直接写在Thread的run()方法中然后调用.start(),而要借助Handler,把更新数据语句写在的post()方法中,再调用.start()。否则会卡顿严重。

代码:

private Runnable mainRunnable = new Runnable() {

   //更新信息
    @Override
    public void run() {

        time = System.currentTimeMillis();
        MainActivity.mcpumemssage=cpu1;

        tvDate.setText(mobileInfor.getForename()+'\n'+mobileInfor.getAllcpu()+'\n'+mobileInfor.getThiscpu()+'\n'+mobileInfor.getFlow()+'\n'+mobileInfor.getmemoryinfo()+'\n'
        +"内存限制:"+mobileInfor.getmemorylimit()+"M"+'\n'+"电量/总电量:"+MainActivity.intLevel+"/"+MainActivity.intScale);

        try{
   
            if(MainActivity.StartorEnd%2==1){
            
            }
        } catch (Exception e){
            Log.e("异常","开始");
            e.printStackTrace();
            Log.e("异常","结束");
        } finally {

        }
 
    }
};

//重写run方法
@Override
public void run() {

   // mainactivity.registerReceiver(mBatInfoReveiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    mobileInfor = new MobileInfor(mainactivity,mActivityManager);

    //计算数据
    do {
        try {
            Thread.sleep(2000);
            }
            mobileInfor.setAllcpu();
            mobileInfor.setForename();
            mobileInfor.setPackagename();
            mobileInfor.setUid();
            mobileInfor.setForepid();
            mobileInfor.setMemoryinfo();
            mobileInfor.setThiscpu();
            mobileInfor.getRate();
            mobileInfor.setFlow();
            mobileInfor.setThismemory();
            mHandler.post(mainRunnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } while (true);
}

然后在主界面(MainActivity)调用,开始线程即可:

CPUThread cpuThread = new CPUThread(mtextView,this,mActivityManager);
cpuThread.start();

9. 把数据导入数据库:

导入数据库是指以数据库文件.db形式导入到手机sd卡中,没有安装查看软件的手机无法直接点开.db文件查看,但是可以方柏霓导出到其他设备进行分析。

因为信息是实时更新,实时更新是在线程中实现的,所以导入数据库的语句也写在线程中。

把7.中的run方法稍作修改:

private Runnable mainRunnable = new Runnable() {

   //更新信息
    @Override
    public void run() {

        time = System.currentTimeMillis();
        MainActivity.mcpumemssage=cpu1;

        tvDate.setText(mobileInfor.getForename()+'\n'+mobileInfor.getAllcpu()+'\n'+mobileInfor.getThiscpu()+'\n'+mobileInfor.getFlow()+'\n'+mobileInfor.getmemoryinfo()+'\n'
        +"内存限制:"+mobileInfor.getmemorylimit()+"M"+'\n'+"电量/总电量:"+MainActivity.intLevel+"/"+MainActivity.intScale);

        try{
            //名为String的名值对
            ContentValues cv = new ContentValues();

            //获取时间
            SimpleDateFormat dff = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            dff.setTimeZone(TimeZone.getTimeZone("GMT+08"));
            String ee = dff.format(new Date());

                //存在数据库中
                cv.put("Times",ee);
                cv.put("Name",mobileInfor.getForename());
                cv.put("Packagename",mobileInfor.getForename());
                cv.put("Pid",mobileInfor.getForepid());
                cv.put("ALL_CPU", mobileInfor.getAllcpu());
                cv.put("Rate",mobileInfor.getRate());  //当前进程cpu/总cpu
                cv.put("THIS_CPU", mobileInfor.getThiscpu());
                cv.put("THIS_Flow",mobileInfor.getdbflow()); //当前进程消耗原始流量
                cv.put("THIS_Memory",mobileInfor.getdbthismemory()); //当前进程占用原始内存
                cv.put("Memory_Limit",mobileInfor.getmemorylimit());
                
                long insert = mysql.insert("mobile", null, cv);
                Log.e("sqlite","success"); //打印日志标志导入结束
            }
        } catch (Exception e){
            Log.e("异常","开始");
            e.printStackTrace();
            Log.e("异常","结束");
        } finally {

        }
     
    }
};

//重写run方法
@Override
public void run() {

   // mainactivity.registerReceiver(mBatInfoReveiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    mobileInfor = new MobileInfor(mainactivity,mActivityManager);

    //计算数据
    do {
        try {
            Thread.sleep(2000);

            //StartorEnd=0结束,关闭游标、数据库
            if(MainActivity.StartorEnd%2==0){
                if(mysql!=null)mysql.close();
            }
            //开始录入数据,创建数据库
            else if (MainActivity.StartorEnd%2==1){  //我添加了一个控制开始和停止导入数据的按钮,每点击一次MainActivity.StartorEnd+1,奇偶交替变化,如果不想加这个控制按钮,该if条件可删去
                //保存到sd卡
                if (!path.exists()) {// 目录存在返回false
                    path.mkdirs();// 创建一个目录
                }
                if (!f.exists()) {// 文件存在返回false
                    try {
                        f.createNewFile();// 创建文件
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                //创建实例
                mysql = SQLiteDatabase.openOrCreateDatabase(f, null);

                //创建数据库
                String sql = "create table if not exists mobile(_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,Times Text,Name Text,Packagename Text,Pid Text,All_CPU Text,Rate Text,THIS_CPU Text,THIS_Flow Text,THIS_Memory Text,Memory_Limit Text)";
                System.out.println("创建:" + sql);
                //执行
                mysql.execSQL(sql);
            }
            mobileInfor.setAllcpu();
            mobileInfor.setForename();
            mobileInfor.setPackagename();
            mobileInfor.setUid();
            mobileInfor.setForepid();
            mobileInfor.setMemoryinfo();
            mobileInfor.setThiscpu();
            mobileInfor.getRate();
            mobileInfor.setFlow();
            mobileInfor.setThismemory();
            mHandler.post(mainRunnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } while (true);
}

10. 把数据以.txt形式导入手机sd卡:

比数据库文件形式导入更简单:

//字符串txt保存在sd卡
public static void stringTxt(String str){
    try {
        FileWriter fw = new FileWriter("/sdcard/himi" + "/himi.txt");//SD卡中的路径
        fw.flush();
        fw.write(str);
        fw.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

仍然在线程中修改,添加如下语句:

//.txt转存在手机中
result = result + ee + "   "+mobileInfor.getForename()+"                   "+mobileInfor.getPackagename()+"               "+mobileInfor.getForepid()+"               "+mobileInfor.getAllcpu()+"          "+mobileInfor.getRate()+"               "+mobileInfor.getThiscpu()+"          "+mobileInfor.getdbflow()+"          "+mobileInfor.getThisMemory()+"          "+mobileInfor.getmemorylimit()+'\n';
stringTxt(result);
Log.e("txt","成功");

修改后的导入.db和.txt文件到sd卡的线程代码:

private Runnable mainRunnable = new Runnable() {

//更新信息
  @Override
  public void run() {

      time = System.currentTimeMillis();
      MainActivity.mcpumemssage=cpu1;

      tvDate.setText(mobileInfor.getForename()+'\n'+mobileInfor.getAllcpu()+'\n'+mobileInfor.getThiscpu()+'\n'+mobileInfor.getFlow()+'\n'+mobileInfor.getmemoryinfo()+'\n'
      +"内存限制:"+mobileInfor.getmemorylimit()+"M"+'\n'+"电量/总电量:"+MainActivity.intLevel+"/"+MainActivity.intScale);

      try{
          //名为String的名值对
          ContentValues cv = new ContentValues();

          //获取时间
          SimpleDateFormat dff = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          dff.setTimeZone(TimeZone.getTimeZone("GMT+08"));
          String ee = dff.format(new Date());

          Log.e("txt","要开始2txt");

          //写入部分,StartOrEnd==1执行录入信息
          if(MainActivity.StartorEnd%2==1){
              //转存在手机中
              result = result + ee + "   "+mobileInfor.getForename()+"                   "+mobileInfor.getPackagename()+"               "+mobileInfor.getForepid()+"               "+mobileInfor.getAllcpu()+"          "+mobileInfor.getRate()+"               "+mobileInfor.getThiscpu()+"          "+mobileInfor.getdbflow()+"          "+mobileInfor.getThisMemory()+"          "+mobileInfor.getmemorylimit()+'\n';
              stringTxt(result);
              Log.e("txt","成功");

              //存在数据库中
              cv.put("Times",ee);
              cv.put("Name",mobileInfor.getForename());
              cv.put("Packagename",mobileInfor.getForename());
              cv.put("Pid",mobileInfor.getForepid());
              cv.put("ALL_CPU", mobileInfor.getAllcpu());
              cv.put("Rate",mobileInfor.getRate());  //当前进程cpu/总cpu
              cv.put("THIS_CPU", mobileInfor.getThiscpu());
              cv.put("THIS_Flow",mobileInfor.getdbflow()); //当前进程消耗原始流量
              cv.put("THIS_Memory",mobileInfor.getdbthismemory()); //当前进程占用原始内存
              cv.put("Memory_Limit",mobileInfor.getmemorylimit());
              
              long insert = mysql.insert("mobile", null, cv);
              Log.e("sqlite","success");
          }
      } catch (Exception e){
          Log.e("异常","开始");
          e.printStackTrace();
          Log.e("异常","结束");
      } finally {

      }
      Log.i(TAG,"time print " +(System.currentTimeMillis() - time));
      Log.e("StartOrEnd",MainActivity.StartorEnd+"");
  }
};

//重写run方法
@Override
public void run() {

 // mainactivity.registerReceiver(mBatInfoReveiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
  mobileInfor = new MobileInfor(mainactivity,mActivityManager);

  //计算数据
  do {
      try {
          Thread.sleep(2000);

          //StartorEnd=0结束,关闭游标、数据库
          if(MainActivity.StartorEnd%2==0){
              if(mysql!=null)mysql.close();
          }
          //开始录入数据,创建数据库
          else if (MainActivity.StartorEnd%2==1){
              //保存到sd卡
              if (!path.exists()) {// 目录存在返回false
                  path.mkdirs();// 创建一个目录
              }
              if (!f.exists()) {// 文件存在返回false
                  try {
                      f.createNewFile();// 创建文件
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              //创建实例
              mysql = SQLiteDatabase.openOrCreateDatabase(f, null);

              //创建数据库
              String sql = "create table if not exists mobile(_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,Times Text,Name Text,Packagename Text,Pid Text,All_CPU Text,Rate Text,THIS_CPU Text,THIS_Flow Text,THIS_Memory Text,Memory_Limit Text)";
              System.out.println("创建:" + sql);
              //执行
              mysql.execSQL(sql);
          }
          mobileInfor.setAllcpu();
          mobileInfor.setForename();
          mobileInfor.setPackagename();
          mobileInfor.setUid();
          mobileInfor.setForepid();
          mobileInfor.setMemoryinfo();
          mobileInfor.setThiscpu();
          mobileInfor.getRate();
          mobileInfor.setFlow();
          mobileInfor.setThismemory();
          mHandler.post(mainRunnable);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  } while (true);
}

测试手机/某app性能的小工具完成。

以上代码全部完整,工程代码已上传。

仍在入门,如有错误,欢迎指出

主要参考:https://www.jianshu.com/p/6bf564f7cdf0 (简书 作者:隋胖胖LoveFat)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 在 Android Studio 的性能测试中,电量的消耗是一个非常重要的指标。这是因为在执行性能测试时,设备的 CPU 和 GPU 会处于高负荷状态,这会导致设备的电量消耗非常快。如果你的应用程序或代码在执行时会导致设备电量消耗过快,那么这就是一个性能问题。 为了进行电量测试,你可以使用 Android Studio 自带的电量监控工具。在 Android Studio 的左下角有一个 "Android" 面板,点击它可以打开 Android 工具窗口。在工具窗口的顶部有一个 "Profiling" 选项卡,点击它可以打开性能监控面板。在性能监控面板中,你可以找到 "Energy" 选项卡,点击它可以打开电量监控工具。 在电量监控工具中,你可以选择要监控的设备和应用程序。一旦开始监控,你就可以看到设备的电量消耗情况。你可以使用电量监控工具来测试你的应用程序或代码在执行时的电量消耗情况,并对其进行优化。 ### 回答2: Android Studio 性能测试电量测试主要是为了评估在运行应用程序时设备的电量消耗情况。通过测试,可以得出一些结论,以指导开发人员对应用程序进行优化,以减少电量消耗。 在进行电量测试之前,需要确保设备已经充满电,并且连接到电源适配器。这样可以保证在测试过程中电量不会因为过度消耗而导致设备关闭。 电量测试可以通过多种方式进行,其中一种常用的方法是使用ADB(Android Debug Bridge)命令来监测设备的电量变化。使用ADB命令可以获取电量相关的信息,例如当前电量百分比、电池健康状况以及电池温度等。通过在应用程序运行过程中重复执行ADB命令,并记录每次的电量变化,就可以得到一个较为准确的电量消耗情况。 除了使用ADB命令监测电量,还可以使用一些电量测试工具来辅助测试。这些工具通常提供了更丰富的电量数据分析功能,例如显示电量消耗的趋势图、识别出耗电量较大的应用程序等。通过这些工具,开发人员可以更直观地了解应用程序在不同场景下的电量消耗情况,并提供优化的建议。 总体来说,Android Studio提供了一些基本的电量测试功能,帮助开发人员评估应用程序的电量消耗情况。但需要注意的是,电量测试只是评估电量消耗的一种手段,开发人员还需要结合其他性能测试方法来全面评估应用程序的性能表现。 ### 回答3: 在Android Studio中进行性能测试时,电量的消耗是一个重要的考量因素。测试过程中,如果测试应用程序消耗大量的电量,可能会造成用户手机电量的快速耗尽,从而影响用户的使用体验。 为了开展电量测试,可以使用Android Studio提供的一些工具和方法。首先,可以通过Android Profiler来监控应用程序在不同情况下的电量消耗情况。可以通过让应用程序在模拟器或实际设备上运行并进行监控,从而获取电量消耗的数据Android Profiler提供了电量指标,可以显示应用程序在不同活动和操作下的电量使用情况,这样可以帮助开发人员分析应用程序的电量问题。 此外,可以使用Android Studio提供的性能测试工具,如Monkey Test和Battery Historian等工具来测试应用程序的电量消耗情况。Monkey Test可以模拟真实用户的随机操作,帮助开发人员找出应用程序可能存在的电量问题。Battery Historian则可以提供详细的电量消耗历史记录,以帮助开发人员分析应用程序的电量消耗模式。 在进行性能测试时,还可以结合其他常见的性能测试方法,如压力测试和持续运行测试,来检查应用程序的电量消耗情况。这些测试方法可以模拟用户的实际使用场景,帮助开发人员更全面地评估应用程序的电量性能。 总的来说,Android Studio提供了丰富的工具和方法来进行电量测试。通过针对电量消耗进行性能测试,开发人员可以发现并解决应用程序中的电量问题,从而提升应用程序的性能和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值