原文:http://p.codekk.com/detail/Android/wenmingvs/AndroidProcess
提供 6 种方法来判断 App 处于前台还是后台,并且封装成工具类供大家使用
最后一种方法堪称 Android 黑科技(非原创),既可以突破 Android5.0 以上的权限封锁,获取任意前台 App 的包名,又不需要权限,此方法并非我原创,原作者是国外的大神,GitHub 项目在这里,也一并加入到工程中,供大家做全面的参考选择
2016.2.11 更新------感谢@EffectiveMatrix大神带来的新的判断前后台的方法
百度网盘
传入 Context 参数与想要判断是否位于前台的 App 的包名,会返回 ture 或者 false 表示 App 是否位于前台
//六种方法任选其一
//使用方法一
Boolean isForeground = BackgroundUtil.getRunningTask(context, packageName);
//使用方法二
Boolean isForeground = BackgroundUtil.getRunningAppProcesses(context, packageName);
//使用方法三
Boolean isForeground = BackgroundUtil.getApplicationValue(context);
//使用方法四
Boolean isForeground = BackgroundUtil.queryUsageStats(context, packageName);
//使用方法五
Boolean isForeground = BackgroundUtil.getFromAccessibilityService(context, packageName);
//使用方法六
Boolean isForeground = BackgroundUtil.getLinuxCoreInfo(context, packageName);
方法 | 判断原理 | 需要权限 | 可以判断其他应用位于前台 | 特点 |
---|---|---|---|---|
方法一 | RunningTask | 否 | Android4.0 系列可以,5.0 以上机器不行 | 5.0 此方法被废弃 |
方法二 | RunningProcess | 否 | 当 App 存在后台常驻的 Service 时失效 | 无 |
方法三 | ActivityLifecycleCallbacks | 否 | 否 | 简单有效,代码最少 |
方法四 | UsageStatsManager | 是 | 是 | 需要用户手动授权 |
方法五 | 通过 Android 无障碍功能实现 | 否 | 是 | 需要用户手动授权 |
方法六 | 读取/proc 目录下的信息 | 否 | 是 | 当 proc 目录下文件夹过多时,过多的 IO 操作会引起耗时 |
原理
当一个 App 处于前台的时候,会处于 RunningTask 的这个栈的栈顶,所以我们可以取出 RunningTask 的栈顶的任务进程,看他与我们的想要判断的 App 的包名是否相同,来达到效果
缺点
getRunningTask 方法在 Android5.0 以上已经被废弃,只会返回自己和系统的一些不敏感的 task,不再返回其他应用的 task,用此方法来判断自身 App 是否处于后台,仍然是有效的,但是无法判断其他应用是否位于前台,因为不再能获取信息
原理
通过 runningProcess 获取到一个当前正在运行的进程的 List,我们遍历这个 List 中的每一个进程,判断这个进程的一个 importance 属性是否是前台进程,并且包名是否与我们判断的 APP 的包名一样,如果这两个条件都符合,那么这个 App 就处于前台
缺点:
在聊天类型的 App 中,常常需要常驻后台来不间断的获取服务器的消息,这就需要我们把 Service 设置成 START_STICKY,kill 后会被重启(等待 5 秒左右)来保证 Service 常驻后台。如果 Service 设置了这个属性,这个 App 的进程就会被判断是前台,代码上的表现就是 appProcess.importance 的值永远是 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,这样就永远无法判断出到底哪个是前台了。
原理
AndroidSDK14 在 Application 类里增加了 ActivityLifecycleCallbacks,我们可以通过这个 Callback 拿到 App 所有 Activity 的生命周期回调。
public interface ActivityLifecycleCallbacks {
void onActivityCreated(Activity activity, Bundle savedInstanceState);
void onActivityStarted(Activity activity);
void onActivityResumed(Activity activity);
void onActivityPaused(Activity activity);
void onActivityStopped(Activity activity);
void onActivitySaveInstanceState(Activity activity, Bundle outState);
void onActivityDestroyed(Activity activity);
}
知道这些信息,我们就可以用更官方的办法来解决问题,当然还是利用方案二里的 Activity 生命周期的特性,我们只需要在 Application 的 onCreate()里去注册上述接口,然后由 Activity 回调回来运行状态即可。
可能还有人在纠结,我用 back 键切到后台和用 Home 键切到后台,一样吗?以上方法适用吗?在 Android 应用开发中一般认为 back 键是可以捕获的,而 Home 键是不能捕获的(除非修改 framework),但是上述方法从 Activity 生命周期着手解决问题,虽然这两种方式的 Activity 生命周期并不相同,但是二者都会执行 onStop();所以并不关心到底是触发了哪个键切入后台的。另外,Application 是否被销毁,都不会影响判断的正确性
原理
通过使用 UsageStatsManager 获取,此方法是 Android5.0 之后提供的新 API,可以获取一个时间段内的应用统计信息,但是必须满足一下要求
使用前提
非常感谢@EffectiveMatrix大神带来的新的判断前后台的方法
此方法属于他原创,具体的博文参照这里http://effmx.com/articles/tong-guo-android-fu-zhu-gong-neng-accessibility-service-jian-ce-ren-yi-qian-tai-jie-mian/
此方法无法直观的通过下拉通知视图来进行前后台的观察,请到 LogCat 中进行观察即可,以下是 LogCat 中打印的信息
原理
Android 辅助功能(AccessibilityService) 为我们提供了一系列的事件回调,帮助我们指示一些用户界面的状态变化。 我们可以派生辅助功能类,进而对不同的 AccessibilityEvent 进行处理。 同样的,这个服务就可以用来判断当前的前台应用
优势
劣势
此方法并非我原创,原作者是国外的大神,GitHub 项目在这里,也一并加入到工程中,供大家做全面的参考选择
原理
无意中看到乌云上有人提的一个漏洞,Linux 系统内核会把 process 进程信息保存在/proc 目录下,Shell 命令去获取的他,再根据进程的属性判断是否为前台
优点
缺点
用法
获取一系列正在运行的 App 的进程
List processes = ProcessManager.getRunningAppProcesses();
获取任一正在运行的 App 进程的详细信息
AndroidAppProcess process = processes.get(location);
String processName = process.name;
Stat stat = process.stat();
int pid = stat.getPid();
int parentProcessId = stat.ppid();
long startTime = stat.stime();
int policy = stat.policy();
char state = stat.state();
Statm statm = process.statm();
long totalSizeOfProcess = statm.getSize();
long residentSetSize = statm.getResidentSetSize();
PackageInfo packageInfo = process.getPackageInfo(context, 0);
String appName = packageInfo.applicationInfo.loadLabel(pm).toString();
判断是否在前台
if (ProcessManager.isMyProcessInTheForeground()) {
// do stuff
}
获取一系列正在运行的 App 进程的详细信息
List processes = ProcessManager.getRunningAppProcessInfo(ctx);