为了检查应用是不是因为占用内存过高导致被系统杀死的,需要记录设备中运行的哪些进程,以及占用的内存情况, 需要将信息每间隔一段时间写入到文件中,以便后面的分析。
在网上找了一些相关的解决方案:
1.通过adb命令来获取进程信息;
adb shell dumpsys meminfo 、 adb shell procrank 、 adb shell + top -n 1等命令都可以查看设备上的进程信息。将命令的结果重定向到文件中,然后对结果进行处理,可记录在不同时间点上设备上运行的进程信息,下面是用python实现的一段代码:
# -*- coding: utf-8 -*-
import os
import time
from apscheduler.schedulers.blocking import BlockingScheduler
# execute command, and return the output
def execCmd(cmd):
r = os.popen(cmd)
text = r.read()
r.close()
return text
# write "data" to file-filename
def writeFile(filename, data, type):
f = open(filename, type)
f.write(data)
f.close()
def saveToFile(infile,outfile,type):
f = open(infile,'r')
flag=0
t=time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
result="record time:"+t+'\t\r\n'
for eachLine in f:
if "Total PSS by process" in eachLine:
flag=1
continue
if flag:
result=result+eachLine.strip()+'\r\n'
if eachLine.strip()=='':
flag=0
writeFile(outfile,result,type)
print result.decode('gbk')
def task():
cmd = "adb shell dumpsys meminfo >1.txt"
result = execCmd(cmd)
# writeFile("1.txt",result,"w")
saveToFile("1.txt","2.txt","a")
if __name__ == '__main__':
scheduler = BlockingScheduler()
scheduler.add_job(task, 'interval', seconds=3)
#scheduler.add_job(task,'cron', seconds=3)
print 'Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C')
try:
scheduler.start()
except KeyboardInterrupt,SystemExit:
scheduler.shutdown()
由于python程序运行在PC上, 需要通过adb连接设备, 不符合要求。
2. 通过ActivityManager 获取
ActivityManager提供了getRunningAppProcesses() API可以获取到进程信息 。 配合PackageManager 可以的得到进程对应的应用的相关信息,入应用名、应用图标等。主要代码如下
private void collectData(){
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
PackageManager pm = getPackageManager();
List<ActivityManager.RunningAppProcessInfo> lists=am.getRunningAppProcesses();
AppUtils proutils=new AppUtils(this);
if(lists.isEmpty() || lists.size() == 0){
return ;
}
for (ActivityManager.RunningAppProcessInfo info : lists) {
ApplicationInfo app = proutils.getApplicationInfo(info.processName);
if (app == null ) continue;
// 计算应用所占内存大小
int[] myMempid = new int[] { info.pid };
Debug.MemoryInfo[] memoryInfo = am.getProcessMemoryInfo(myMempid);
double memSize = memoryInfo[0].getTotalPss();
...
}
...
}
getRunningAppProcesses()在Android 5.0+ 的系统上仅返回本应用进程,所以该方法只适用于5.0 以下的设备。
3. 通过读取/proc的内容实现
在github上找到一个相关库 ,它通过读取设备中/proc的内容来获取运行的进程信息 。
由于google在权限方面的限制,在7.0+的系统上,无法读取/proc文件的内容, 因此该方法也仅适用于7.0以下的设备
4. 通过UsageStatsManager获取
UsageStatsManager是5.0 才开始有的,用于记录应用的使用信息, 入应用在某段时间内处于前台和后台的时间,最近一次启动的时间等。
- 使用它之前需要在清单文件中配置 “android.permission.PACKAGE_USAGE_STATS”的权限
- 用户必须在 设置–安全–有权查看使用情况的应用 中勾选相应的应用
- 魅族和小米手机不能通过UsageStatsManager获取应用使用情况
下面是使用UsageStatsManager 的例子:
UsageStatsManager usm = (UsageStatsManager) context.getSystemService("usagestats");
Calendar calendar = Calendar.getInstance();
long endTime = calendar.getTimeInMillis();
calendar.add(Calendar.MINUTE, -1);
long startTime = calendar.getTimeInMillis();
List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,startTime,endTime);
for (UsageStats info : appList) {
ApplicationInfo app = proutils.getApplicationInfo(info.getPackageName());
....
}
由于UsageStats 中不包含应用的进程pid,所以这种方法没办法获得占用的内存信息,适用于7.0 以上的系统。
5.通过在android程序中执行shell命令实现
将命令的执行结果写入sdcard中,然后再结果做处理。
public synchronized String run(String [] cmd, String workdirectory) throws IOException {
String result = "";
try {
ProcessBuilder builder = new ProcessBuilder(cmd);
InputStream in = null;
//设置一个路径
if (workdirectory != null) {
builder.directory(new File(workdirectory));
builder.redirectErrorStream(true);
Process process = builder.start();
in = process.getInputStream();
byte[] re = new byte[1024];
while (in.read(re) != -1){
result = result + new String(re);
// System.out.println("result = "+new String(re,"UTF-8"));
}
}
if (in != null) {
in.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return result;
}
//执行
new Thread(new Runnable() {
@Override
public void run() {
try {
String[] args = {"/system/bin/top", "-n", "1"};
String result = cmd.run(args, "/system/bin/");
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
测试发现在7.0 以上的系统上仅能得到top和本应用两个进程的信息,可能也跟权限有关
通过尝试以上的几个方法实现了7.0以下系统的进程信息的统计。这是输出的一个结果截图:
OK! 先这样了吧!