Android 性能測試小工具 (cpu,內存,電量,流量數據浮窗顯示,信息導入數據庫、sd卡)

【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)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章