这段时间做的一个项目,需要获取当前正在运行的app,android5.0之前可以使用getRunningTask获取,5.0这个方法不可用了,但是提供了getRunningAppProcess也可以获得。但是自从android5.1以后,Google从安全和隐私方面考虑,也废弃了这个方法,现在只能返回自己的应用。这段时间一直在研究,在网上也搜寻了很久,诸如,通过反射ActivityManager.RunningAppProcessInfo下的“processState”,或者反射android.app.ActivityThread,或者获取正在运行的top activity也都是失败。还有使用UsageState,AccessableService,这些需要用户手动开启,不符合项目需要。也找过源代码研究比如ActivityManagerNative的,系统设置里的应用管理代码,也都无功而返。

     终于在stackOverFlow找个一个大神的回答http://stackoverflow.com/a/32366476。读取android下proc的文件夹获取进程的相关信息。虽然之前看到过这个大神的获取正在运行的进程列表https://github.com/jaredrummler/AndroidProcesses,但是获取的是列表,不能判断哪一个进程是当然显示的应用,用于判断的foreground参数能返回多个true的情况。这个大神又在回答这个问题放出获取当前应用的代码。贴一下代码:

/** 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,该文件夹下的文件包含这个进程的信息;

2.cgroup,控制组群(control groups)的简写,是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。cpu:设置cpu的使用率;cpuacct:记录cpu的统计信息。

3.bg_non_interactive,运行cpu的一个分组,另一分组是apps,当一个应用(进程)即可从apps分组切换到bg_non_interactive,也可以切换回来。apps分组可以利用95%的cpu,而bg_non_interactive只能使用大约5%。

4.cmdline,显示内核启动的命令行。

5.oom_score_adj,这个文件的数值用来标记在内存不足的情况下,启发式的(不知道怎么翻译好==)选择哪个进程被杀掉,值从0(从不被杀掉)到1000(总是被杀掉)。