android判断前台应用

来源:互联网 发布:江湖风云录脚本软件 编辑:程序博客网 时间:2024/06/06 16:58

转载自: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.  实现原理

AndroidSDK14Application类里增加了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生命周期的特性,我们只需要在ApplicationonCreate()里去注册上述接口,然后由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以上有效

2AndroidManifest中加入此权限

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. 可以用来判断任意应用甚至 ActivityPopupWindow 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层我们平时使用较多的进程属性包括:piduidprocessNamepackageNameimportance等,在自解析方案中只对这些属性进行解析就可以满足大部分需求。

该方案中需要进行解析的设备节点包括:

6.4.  代码缺点

1、  该方案在6.0手机适配运行OK,但在最新的小米、华为6.0.1手机中发现受SELinux的限制,无法读取系统应用的设备节点进行解析,只能解析第三方应用设备节点。

2、  可能存在能耗问题。

6.5.  能耗问题解决

1、  Java层对象缓存:对调用比较频繁的Java层对象在JNI中建立全局缓存,这就避免了每次调用时都需要通过JNI接口获取。


对一些判断是需要的场景在初始化时有Java层传入Jni层,并建立全局缓存:


2、  过来为Android进程:将pid小于1000Native进程过滤掉。

3、  只解析发生变化的进程:在每次轮询解析/proc节点时先判断进程的pid在缓存中是否存在,如果存在只需要更新进程的优先级信息,其他信息不会发生变化;如果进程之前不存在则需要全新解析:

命中缓存时的解析代码如下:

未命中缓存时,则进全新解析:

4、  在解析进程时,过来父进程为zygote的进程:Android中所有应用进程的父进程都是zyote

5、  Java层对调用做缓存处理:对于调用比较频繁的情况,如果当次Native调用没有完成,则之前返回之前的值,不需要堵塞等待。

6、  对于只关心前台进程的场景进行特殊处理:

通过优化后,适配方案的能耗与系统接口基本保持一致:

 

7.   作为系统进程的获取方式

7.1.  技术方案

虽然getRunningTaskAndroid5.0开始被系统废弃,但是作为系统应用时,该接口依然是可用的。在用户取得Root权限,或者应用跟厂商进行合作时,应用本身可能会被内置的系统目录,即:/system/app/system/private-app目录。因此对于这种情况,使用getRunningTask获取依然是一种方便的实现。

7.2.  方案实现

1、  需要判断应用是否为系统应用:

2、  AndroidManifest.xml中需要声明如下权限: