【mie haha的博客】轉載請註明出處(萬分感謝!):
https://blog.csdn.net/qq_40315080/article/details/98610748
寫一個測試各個app/手機性能的小工具app。
功能:
- 打開app得到手機中已有的應用列表,點擊可以跳轉進入。
- 上方標題欄上有選擇菜單,可以選擇打開,刷新,關閉浮窗,浮窗可以拖動。
- 打開浮窗後,浮窗將顯示頂層(當前所在)app的cpu佔用、內存、流量,手機總cpu佔用、電量數據,每幾秒實時更新數據。
- 一個開關按鈕,點擊控制數據導入數據庫和SD卡或停止導入。(可選)
- 一個選擇按鈕,點擊控制監測限定某個app或監測頂層app的數據。(可選)
- 點擊浮窗返回該小工具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)