最新更新:因爲不少國產機修改過內核文件,包括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