转载自:http://blog.csdn.net/zhgxhuaa/article/details/62419006
Andoid系统从Android5.0开始对获取前台进程接口进行相关限制。本文为对突破Android接口限制进行的一系列研究的总结。目前所有获取前台进程的接口有如下7种方式:
接下来将对每一种方案进行详细的阐述。
1.RunningTask
当一个App处于前台的时候,会处于RunningTask的这个栈的栈顶,所以我们可以取出RunningTask的栈顶的任务进程,看他与我们的想要判断的App的包名是否相同,来达到效果。
代码实现如下:
1.2. 方案缺点
getRunningTask方法在Android5.0以上已经被废弃,只会返回自己和系统的一些不敏感的task,不再返回其他应用的task,用此方法来判断自身App是否处于后台,仍然是有效的,但是无法判断其他应用是否位于前台,因为不再能获取信息
2. 通过RunningProcess
2.1. 实现原理
通过runningProcess获取到一个当前正在运行的进程的List,我们遍历这个List中的每一个进程,判断这个进程的一个importance 属性是否是前台进程,并且包名是否与我们判断的APP的包名一样,如果这两个条件都符合,那么这个App就处于前台。
代码实现如下:
2.2. 方案缺点
1、 对于前台Service(通过setForeground接口设置)的进程会被误判判断是前台进程,代码上的表现就是appProcess.importance的值永远是ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,这样就会存在误判的情况出现。
2、 该方案只在Android5.0之前有效,Android5.0以后也被系统废弃。而且基于Android5.0的小米开发版及部分基于Android5.0的华为版本该方案也会存在问题。
针对会在部分Android5.0的机型上存在问题的问题,我封装了一个测试接口用于判断,如果该接口返回为true则说明为问题机型,则RunningProcess方案不适用。测试接口的思想为:先通过getRunningAppProcesses获取到进程的List,然后判断如果进程数量非常少(这里设定的阈值为3个),或者进程列表中只有Launcher和应用自身存在,则认为接口存在问题。测试代码如下:
3. 通过ActivityLifecycleCallbacks
3.1. 实现原理
AndroidSDK14在Application类里增加了ActivityLifecycleCallbacks,我们可以通过这个Callback拿到App所有Activity的生命周期回调。
public interface ActivityLifecycleCallbacks {
voidonActivityCreated(Activity activity, Bundle savedInstanceState);
voidonActivityStarted(Activity activity);
voidonActivityResumed(Activity activity);
voidonActivityPaused(Activity activity);
voidonActivityStopped(Activity activity);
voidonActivitySaveInstanceState(Activity activity, Bundle outState);
voidonActivityDestroyed(Activity activity);
}
知道这些信息,我们就可以用更官方的办法来解决问题,当然还是利用方案二里的Activity生命周期的特性,我们只需要在Application的onCreate()里去注册上述接口,然后由Activity回调回来运行状态即可。
3.2. 方案特点
1、 在Android应用开发中一般认为back键是可以捕获的,而Home键是不能捕获的(除非修改framework),虽然这两种方式的Activity生命周期并不相同,但是二者都会执行onStop();所以并不关心到底是触发了哪个键切入后台的。另外,Application是否被销毁,都不会影响判断的正确性。
2、 该方案除了用于判断当前应用内那个Activity位于前台外,还可以用于作为实现“进程完全退出”的一种很好的技术方案。
3、 该方案需要在Application中进行注册相关Activity生命周期的回调,主要代码如下:
在用该接口判断应用是否在前台时,只需要对activityCount计数进行判断即可:
4. 通过UsageStatsManager获取
4.1. 实现原理
通过使用UsageStatsManager获取,此方法是Android5.0之后提供的新API,可以获取一个时间段内的应用统计信息,但是必须满足一下要求
使用前提
1、此方法只在android5.0以上有效
2、AndroidManifest中加入此权限
3、手动开启权限:打开手机设置,点击安全-高级,在有权查看使用情况的应用中,为这个App打上勾
该方案的实现代码如下:
4.2. 方案特点
1、 该方案最大的缺点是需要用户手动授权,因此在使用时要结合场景做适当引导。
2、 该方案为Android5.0以后Google官方比较推荐的获取进程信息的方式,是最符合Google意图的方式,不过在使用时会有一些延时需要小心处理。
4.3. 相关辅助接口
1、跳转到“查看应用使用权限”界面的跳转代码如下:
2、判断“查看应用使用权限”是否开启的接口:
5. 通过AccessibilityService获取
5.1. 技术原理
Android 辅助功能(AccessibilityService) 为我们提供了一系列的事件回调,帮助我们指示一些用户界面的状态变化。我们可以派生辅助功能类,进而对不同的AccessibilityEvent进行处理。同样的,这个服务就可以用来判断当前的前台应用
5.2. 方案特点
1. AccessibilityService有非常广泛的 ROM 覆盖,特别是非国产手机,从 API Level 8(Android 2.2) 到API Level 23(Android 6.0)。
2. AccessibilityService不再需要轮询的判断当前的应用是不是在前台,系统会在窗口状态发生变化的时候主动回调,耗时和资源消耗都极小。
3. 不需要权限请求
4. 它是一个稳定的方法,并非利用 Android 一些设计上的漏洞,可以长期使用的可能很大。
5. 可以用来判断任意应用甚至 Activity,PopupWindow, Dialog 对象是否处于前台。
5.3. 方案缺点
1、 需要用户手动开启辅助功能。
2、 辅助功能会伴随应用被“强行停止”或第三方管理工具通过Root而剥夺,而且进程重启后需要对用户进行重新引导开启。
3、 部分厂商可能对辅助功能进行限制,如已发现的vivo的部分机型
5.4. 方案实现
1、派生 ACCESSIBILITY SERVICE,创建窗口状态探测服务:
4、 创建 ACCESSIBILITY SERVICE INFO 属性文件:
5、 在AndroidManifes.xml中注册第1步定义的Service:
6、 使用服务判断应用是否在前台:
5.5. 相关辅助接口
1、 跳转到“辅助功能”设置界面的代码如下:
2、判断辅助功能是否开启的代码如下:
6. 自解析/process获取
6.1. 技术原理
Linux系统内核会把process进程信息保存在/proc目录下,通过JNI封装接口,在JNI中去访问并解析相关进程的设备节点,再根据进程的属性判断是否为前台
6.2. 方案特点
1、不需要任何权限。
2、可以判断任意一个应用是否在前台,而不局限在自身应用。
3、当/proc下文件夹过多时,此方法是耗时操作。
4、该方案存在能耗问题。
5、在Android6.0.1以上版本或部分厂商版本受限于SEAndroid,只能获取到第三方进程的信息。
6.3. 代码实现
在Android层我们平时使用较多的进程属性包括:pid、uid、processName、packageName、importance等,在自解析方案中只对这些属性进行解析就可以满足大部分需求。
该方案中需要进行解析的设备节点包括:
6.4. 代码缺点
1、 该方案在6.0手机适配运行OK,但在最新的小米、华为6.0.1手机中发现受SELinux的限制,无法读取系统应用的设备节点进行解析,只能解析第三方应用设备节点。
2、 可能存在能耗问题。
6.5. 能耗问题解决
1、 Java层对象缓存:对调用比较频繁的Java层对象在JNI中建立全局缓存,这就避免了每次调用时都需要通过JNI接口获取。
对一些判断是需要的场景在初始化时有Java层传入Jni层,并建立全局缓存:
2、 过来为Android进程:将pid小于1000的Native进程过滤掉。
3、 只解析发生变化的进程:在每次轮询解析/proc节点时先判断进程的pid在缓存中是否存在,如果存在只需要更新进程的优先级信息,其他信息不会发生变化;如果进程之前不存在则需要全新解析:
命中缓存时的解析代码如下:
未命中缓存时,则进全新解析:
4、 在解析进程时,过来父进程为zygote的进程:Android中所有应用进程的父进程都是zyote。
5、 在Java层对调用做缓存处理:对于调用比较频繁的情况,如果当次Native调用没有完成,则之前返回之前的值,不需要堵塞等待。
6、 对于只关心前台进程的场景进行特殊处理:
通过优化后,适配方案的能耗与系统接口基本保持一致:
7. 作为系统进程的获取方式
7.1. 技术方案
虽然getRunningTask从Android5.0开始被系统废弃,但是作为系统应用时,该接口依然是可用的。在用户取得Root权限,或者应用跟厂商进行合作时,应用本身可能会被内置的系统目录,即:/system/app或/system/private-app目录。因此对于这种情况,使用getRunningTask获取依然是一种方便的实现。
7.2. 方案实现
1、 需要判断应用是否为系统应用:
2、 在AndroidManifest.xml中需要声明如下权限: