最新更新:因为不少国产机修改过内核文件,包括proc下文件结构,因此本文章的方法再也不适合用来判断后台进程的运行。android
这段时间作的一个项目,须要获取当前正在运行的app,android5.0以前可使用getRunningTask获取,5.0这个方法不可用了,可是提供了getRunningAppProcess也能够得到。可是自从android5.1之后,Google从安全和隐私方面考虑,也废弃了这个方法,如今只能返回本身的应用。这段时间一直在研究,在网上也搜寻了好久,诸如,经过反射ActivityManager.RunningAppProcessInfo下的“processState”,或者反射android.app.ActivityThread,或者获取正在运行的top activity也都是失败。还有使用UsageState,AccessableService,这些须要用户手动开启,不符合项目须要。也找过源代码研究好比ActivityManagerNative的,系统设置里的应用管理代码,也都无功而返。git
终于在stackOverFlow找个一个大神的回答http://stackoverflow.com/a/32366476。读取android下proc的文件夹获取进程的相关信息。虽然以前看到过这个大神的获取正在运行的进程列表https://github.com/jaredrummler/AndroidProcesses,可是获取的是列表,不能判断哪个进程是固然显示的应用,用于判断的foreground参数能返回多个true的状况。这个大神又在回答这个问题放出获取当前应用的代码。贴一下代码:github
/** first app user */ public static final int AID_APP = 10000; /** offset for uid ranges for each user */ public static final int AID_USER = 100000; public static String getForegroundApp() { File[] files = new File("/proc").listFiles(); int lowestOomScore = Integer.MAX_VALUE; String foregroundProcess = null; for (File file : files) { if (!file.isDirectory()) { continue; } int pid; try { pid = Integer.parseInt(file.getName()); } catch (NumberFormatException e) { continue; } try { String cgroup = read(String.format("/proc/%d/cgroup", pid)); String[] lines = cgroup.split("\n"); String cpuSubsystem; String cpuaccctSubsystem; if (lines.length == 2) {//有的手机里cgroup包含2行或者3行,咱们取cpu和cpuacct两行数据 cpuSubsystem = lines[0]; cpuaccctSubsystem = lines[1]; }else if(lines.length==3){ cpuSubsystem = lines[0]; cpuaccctSubsystem = lines[2]; }else { continue; } if (!cpuaccctSubsystem.endsWith(Integer.toString(pid))) { // not an application process continue; } if (cpuSubsystem.endsWith("bg_non_interactive")) { // background policy continue; } String cmdline = read(String.format("/proc/%d/cmdline", pid)); if (cmdline.contains("com.android.systemui")) { continue; } int uid = Integer.parseInt( cpuaccctSubsystem.split(":")[2].split("/")[1].replace("uid_", "")); if (uid >= 1000 && uid <= 1038) { // system process continue; } int appId = uid - AID_APP; int userId = 0; // loop until we get the correct user id. // 100000 is the offset for each user. while (appId > AID_USER) { appId -= AID_USER; userId++; } if (appId < 0) { continue; } // u{user_id}_a{app_id} is used on API 17+ for multiple user account support. // String uidName = String.format("u%d_a%d", userId, appId); File oomScoreAdj = new File(String.format("/proc/%d/oom_score_adj", pid)); if (oomScoreAdj.canRead()) { int oomAdj = Integer.parseInt(read(oomScoreAdj.getAbsolutePath())); if (oomAdj != 0) { continue; } } int oomscore = Integer.parseInt(read(String.format("/proc/%d/oom_score", pid))); if (oomscore < lowestOomScore) { lowestOomScore = oomscore; foregroundProcess = cmdline; } } catch (IOException e) { e.printStackTrace(); } } return foregroundProcess; } private static String read(String path) throws IOException { StringBuilder output = new StringBuilder(); BufferedReader reader = new BufferedReader(new FileReader(path)); output.append(reader.readLine()); for (String line = reader.readLine(); line != null; line = reader.readLine()) { output.append('\n').append(line); } reader.close(); return output.toString().trim();//不调用trim(),包名后面会带有乱码 }
依照大神的代码,在实际测试中有的手机能返回固然的包名,有的仍是返回null,比照系统文件和代码分析,发现有的手机里cgroup包含两行cpu 和cpuacct,有的则是三行,多了一行memory。因此对代码稍加改动,上面是改动过的。下面对调用的文件和文件内容解释一下:安全
1.proc下以数字命名的文件夹,文件夹名便是一个进程的pid,该文件夹下的文件包含这个进程的信息;app
2.cgroup,控制组群(control groups)的简写,是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。cpu:设置cpu的使用率;cpuacct:记录cpu的统计信息。oop
3.bg_non_interactive,运行cpu的一个分组,另外一分组是apps,当一个应用(进程)便可从apps分组切换到bg_non_interactive,也能够切换回来。apps分组能够利用95%的cpu,而bg_non_interactive只能使用大约5%。测试
4.cmdline,显示内核启动的命令行。ui
5.oom_score_adj,这个文件的数值用来标记在内存不足的状况下,启发式的(不知道怎么翻译好==)选择哪一个进程被杀掉,值从0(从不被杀掉)到1000(老是被杀掉)。spa