获取顶端包名功能在Android 5.0系统前后发生重大变化,经过各种搬运加测试后,得出一些通用解决方案,虽然没有一种简单通用的方法,但是目前已经有可以兼容所有系统的方案。
Android 5.0 之前
直接通过getRunningTasks方法进行获取
用法
需要添加下面权限:
1 | <uses-permission android:name="android.permission.GET_TASKS" /> |
1 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); |
结论
- 通过上面的方法,我们就可以获取到当前显示在顶端的包名,很多锁屏类,或者应用锁的核心就是这块
- 可惜的是5.0(API > 20) 之后就不能再用这个方法了,根据下面的官方说明,Android 5.0系统之后
getRunningTasks
方法被弃用了,经过实际测试之后,这个方法仅仅只能返回自身应用信息以及一些公开包名的信息(如:桌面,设置等)
Android 5.0 之后
通过getRunningAppProcesses方法进行获取
原理:通过获取当前在顶端运行的进程,遍历该进程下的所有包名,得出该进程下的包名列表
使用方法
1 | /** |
结论
- 正如原理所描述,它返回的是当前在顶端运行的进程的包名列表,正常情况下,我们开发的时候都是一个进程对应一个包名,但是如果是大公司的话,可能会将旗下所有应用共享同一个进程,于是这里就会返回一个包名数组,所以这个方法只能说满足大部分日常需求
- 经过最近的测试,还发现了在一些新的机器上(国产为主,代表:小米 v5.0.2系统)已经不能再使用这种方案了,因为这个方法在这些机器上,只能获取到自己应用的信息,变得和
getRunningTasks
在Android 5.0系统上的表现差不多 - 需要继续寻找新方案…
通过Android 5.0提供的新API进行获取
Android 5.0之后提供了一个新的API: UsageStatsManager
,通过该API我们可以准确获取到当前顶端的包名信息
使用方法
需要添加下面的权限:
- 添加下面权限:
1 | <uses-permission |
- 引导用户到设置页面为自身应用开启 有权查看使用情况的应用 的权限:
1 | Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); |
- 通过下面的代码即可获取顶端包名:
1 | /** |
如果希望能检测用户是否已经允许本应用有权查看组件使用情况,可以通过下面代码进行检查:
1 | if (Build.VERSION.SDK_INT >= 19) { |
结论
- 使用这个方法之前需要用户允许应用有权查看组件使用情况,然后才有数据返回
- 新的API能完美进行,就是需要添加新的权限和做一下用户引导
- 如果获取的时间设置得太短,并且这个时间内又没有发生改变的话,那么是获取不到最新的顶端的包名情况的
- 最重要的是,最近又发现在一些机器上(国产为主,魅族领头,LG G3在列),是没有允许应用有权查看使用组件的情况的设置页面的,于是,我们大喊一声,妈蛋~
- 需要继续寻找新方案…
总结(截止至2015-09-22)
目前还不能全面兼容Android 5.0系统,还需要继续寻找新的方案
后续尝试
尝试dunpsys命令
如果是在adb shell中使用 dumpsys activity activities
的话,倒是可以获取到顶端的包名,但是代码中的话,除非你拥有和系统应用的签名,否则,是不能使用 android.permission.DUMP
权限的,也就不能使用 dumpsys
命令了
continue…
尝试通过/proc目录进行判断
/proc
目录是存放当前系统运行中的一些信息,包括各个进程,cpu,内存,启动时长等,具体目录介绍可以参照这里,偶然间发现了stackoverflow 上的这个神贴,就是用这个方法的,无须任何权限,但是作者也说到缺乏测试,而我这边自己测试之后发现不是很理想,很多机子都不行
ps:
正常的
cgroup
文件(Nexus4)上结果如下:附上几张在天朝不同机子上的
cgroup
文件解读图:
- 魅族mx4 Flyme OS 4.2.8.2C Android 4.4.2
- 魅族mx4 pro Flyme OS 4.5.5A Android 5.0.1
- 魅族m2 note Flyme OS 4.5.3A Android 5.1
- 小米2s MIUI 5.7.16 Android 5.0.2
continue…
通过accessibility-service服务进行
方法参考这里,经过测试,能够不用在配置新权限的前提下完美运行,但是也有一些问题
- Each user must enable the service via Android’s accessibility settings in order for the AccessibilityEvents to be received.
The service generally runs until the user explicitly disables it. You can make the service stop itself if needed, but I don’t know of any way to programmatically restart it correctly if you’ve stopped it.- When a user tries to enables the AccessibilityService, they can’t press the OK button if an app has placed an overlay on the screen. Some apps that do this are Velis Auto Brightness and Lux. This can be confusing because the user might not know why they can’t press the button or how to work around it.
- The AccessibilityService won’t know the current activity until the first change of activity.
continue…
结语
总结下来,Android 5.0以上的机子目前有4种方法可以尝试,每种方法都有一些优点和缺陷,在找到一个完美的方法之前,我们可以依次采用下面的方法来尽量覆盖多一点机型
getRunningAppProcess
通过观察proc
UsageStatsManager
Accessibility-service
后记&&最终解决方案
在我准备放弃的时候,我又重新看了一下 通过观察proc 获取当前运行中的进程这个方法,发现其核心代码在于通过判断oom-score
来识别当前运行的顶端应用的,这个是什么原理呢?查阅了一下资料,这个涉及到 Linux OOM Killer
机制:
Linux 内核存在一个OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程杀掉,而每个进程都有一个oom_score属性,oomkiller会杀掉oom_score较大的进程
那么究竟杀掉那个进程呢,其实是受下面3个文件的影响:
/proc/$PID/oom_adj
:用于调整oom_score的分数,范围在[-17,15],当为-17时,oom_score将变为0,标识禁止杀死该进程/proc/$PID/oom_score
:标识最终的分数,分数越大,进程越有可能被杀/proc/$PID/oom_score_adj
:从Linux 2.6.36开始都安装了/proc/$PID/oom_score_adj,此后替换为/proc/$PID/oom_adj,即使当前是对/proc/$PID/oom_adj进行的设置,在内核内部进行变换后的值也是针对/proc/$PID/oom_score_adj设置的
3个文件的具体manpage可以参考这里,下面是我截取的一些关键内容
/proc/[pid]/oom_adj (since Linux 2.6.11)
This file can be used to adjust the score used to select which process should be killed in an out-of-memory (OOM) situation. The kernel uses this value for a bit-shift operation of the process’s oom_score value: valid values are in the range -16 to +15, plus the special value -17, which disables OOM-killing altogether for this process. A positive score increases the likelihood of this process being killed by the OOM-killer; a negative score decreases the likelihood.
The default value for this file is 0; a new process inherits its parent’s oom_adj setting. A process must be privileged (CAP_SYS_RESOURCE) to update this file. Since Linux 2.6.36, use of this file is deprecated in favor of /proc/[pid]/oom_score_adj.
/proc/[pid]/oom_score (since Linux 2.6.11)
This file displays the current score that the kernel gives to this process for the purpose of selecting a process for the OOM-killer. A higher score means that the process is more likely to be selected by the OOM-killer. The basis for this score is the amount of memory used by the process, with increases (+) or decreases (-) for factors including:
- whether the process creates a lot of children using fork(2) (+);
- whether the process has been running a long time, or has used a lot of CPU time (-);
- whether the process has a low nice value (i.e., > 0) (+);
- whether the process is privileged (-); and
- whether the process is making direct hardware access (-).
The oom_score also reflects the adjustment specified by the oom_score_adj or oom_adj setting for the process.
/proc/[pid]/oom_score_adj (since Linux 2.6.36)
This file can be used to adjust the badness heuristic used to select which process gets killed in out-of-memory conditions. The badness heuristic assigns a value to each candidate task ranging from 0 (never kill) to 1000 (always kill) to determine which process is targeted. The units are roughly a proportion along that range of allowed memory the process may allocate from, based on an estimation of its current memory and swap use. For example, if a task is using all allowed memory, its badness score will be 1000. If it is using half of its allowed memory, its score will be 500.There is an additional factor included in the badness score: root processes are given 3% extra memory over other tasks.
The amount of “allowed” memory depends on the context in which the OOM-killer was called. If it is due to the memory assigned to the allocating task’s cpuset being exhausted, the allowed memory represents the set of mems assigned to that cpuset (see cpuset(7)). If it is due to a mempolicy’s node(s) being exhausted, the allowed memory represents the set of mempolicy nodes. If it is due to a memory limit (or swap limit) being reached, the allowed memory is that configured limit. Finally, if it is due to the entire system being out of memory, the allowed memory represents all allocatable resources.
The value of oom_score_adj is added to the badness score before it is used to determine which task to kill. Acceptable values range from -1000 (OOM_SCORE_ADJ_MIN) to +1000 (OOM_SCORE_ADJ_MAX). This allows user space to control the preference for OOM-killing, ranging from always preferring a certain task or completely disabling it from OOM killing. The lowest possible value, -1000, is equivalent to disabling OOM- killing entirely for that task, since it will always report a badness score of 0.
Consequently, it is very simple for user space to define the amount of memory to consider for each task. Setting a oom_score_adj value of +500, for example, is roughly equivalent to allowing the remainder of tasks sharing the same system, cpuset, mempolicy, or memory controller resources to use at least 50% more memory. A value of -500, on the other hand, would be roughly equivalent to discounting 50% of the task’s allowed memory from being considered as scoring against the task.
For backward compatibility with previous kernels, /proc/[pid]/oom_adj can still be used to tune the badness score. Its value is scaled linearly with oom_score_adj.
Writing to /proc/[pid]/oom_score_adj or /proc/[pid]/oom_adj will change the other with its scaled value.
通过上面的介绍,估计大家都有些想法了,恩,这是一条可行的路:
- 遍历
/proc
目录下的子目录,排除系统应用uid - 在根据一些其他规则排除一些不符合的应用
- 排序剩下的应用,得出oom_score分数最少的pid
- 根据pid找对应的包名
原理大概如此,基本代码可以参考这里,但是照抄那份代码是不太能在天朝厂商机子上跑通的,需要改进。Anyway,最后我改进代码之后,在我这边手上几台机子测试都没有什么问题,目前也在大规模测试中。
ps. 因为一些原因,最终代码还不太好在现在发放出来
参考资料
- http://developer.android.com/reference/android/app/ActivityManager.html#getRunningTasks(int)
- http://chaoqunz.blog.163.com/blog/static/6154877720090495819383/
- http://blog.csdn.net/qinjuning/article/details/6978560
- http://blog.csdn.net/qinjuning/article/details/7009824
- http://m.blog.csdn.net/blog/zsqxiao/44755621
- http://stackoverflow.com/questions/27689276/how-to-get-running-application-activity-name-in-android-5-0l
- http://stackoverflow.com/questions/27087675/cannot-get-foreground-activity-name-in-android-lollipop-5-0-only
- http://stackoverflow.com/questions/28361629/activitynotfoundexception-in-lollipop-when-trying-to-launch-activity-with-intent
- http://stackoverflow.com/questions/28296633/android-usage-access-for-android-5-samsung-devices
- http://android.stackexchange.com/questions/114525/how-to-access-apps-with-usage-access-setting-on-lg-g3
- http://blog.chinaunix.net/uid-23960192-id-3549140.html
- http://stackoverflow.com/questions/30619349/android-5-1-1-and-above-getrunningappprocesses-returns-my-application-packag
- http://stackoverflow.com/questions/3873659/android-how-can-i-get-the-current-foreground-activity-from-a-service/27642535#27642535
- https://github.com/Yhzhtk/note/issues/31
- http://laoxu.blog.51cto.com/4120547/1267097
- http://man7.org/linux/man-pages/man5/proc.5.html
- http://wushank.blog.51cto.com/3489095/1406765