|
目前主流的监控Foreground Activity的方法,主要有以下三种,各有利弊,还没有找到一个彻底的完美解决方案!
一、使用 AccessibilityService
- 通过 AccessibilityService可以监控当前Active Window
- 通过回调函数 onAccessibilityEven 检查事件类型 TYPE_WINDOW_STATE_CHANGED 获知当前 window 改变了
- 通过调用 PackageManager.getActivityInfo() 检查当前的window 是不是一个Activity
1. 优点:
- 经过测试Android 2.2 (API 8) ~ Android 5.1.0(API 22) 都可以正常工作,更高版本的还没有测试过
- 不需要轮询机制,非常省电
- 不需要申明 GET_TASKS 权限
2. 缺点:
- 用户必须手动在手机的设置页面开启 Android's accessibility settings 才能收到 AccessibilityEvent事件
- 当用户手动在手机设置页面关闭 Accessibility setting后,AccessibilityService就会停止运行。你也可以在service 里面用代码 stop itself,但是一旦在代码里面停止service之后,目前还没有找到什么办法能够再次开启。
- 当用户在设置页面开启 AccessibilityService的时候,有些第三方应用会弹出一个悬浮窗盖住屏幕,导致用户无法点击确认按钮。例如 Velis Auto Brightness 和 Lux 这些App就会干这种事。 目前还不知道如果规避这些第三方应用的恶心行为。
- AccessibilityService 是被动触发的,无法主动获知当前Activity 的信息,除非当前Activity有所改变
3. 代码示例
public class WindowChangeDetectingService extends AccessibilityService {
@Override
protected void onServiceConnected() {
super.onServiceConnected();
//Configure these here for compatibility with API 13 and below.
AccessibilityServiceInfo config = new AccessibilityServiceInfo();
config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
if (Build.VERSION.SDK_INT >= 16)
//Just in case this helps
config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
setServiceInfo(config);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
ComponentName componentName = new ComponentName(
event.getPackageName().toString(),
event.getClassName().toString()
);
ActivityInfo activityInfo = tryGetActivity(componentName);
boolean isActivity = activityInfo != null;
if (isActivity)
Log.i("CurrentActivity", componentName.flattenToShortString());
}
}
private ActivityInfo tryGetActivity(ComponentName componentName) {
try {
return getPackageManager().getActivityInfo(componentName, 0);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
@Override
public void onInterrupt() {}
}
|
6. Enabling the Service每个用户必须手动到手机设置页面开启 AccessibilityService 这个service才会被调用到。查看具体实现:this StackOverflow answer
二、使用 ActivityManager使用 ActivityManager有一定版本限制。官方文档有详细的说明:on API-21 as of LOLLIPOP, ActivityManager.getRunningTasks() is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks, and possibly some other tasks such as home that are known to not be sensitive. 网上有人说可以用下面的方法分别调用不同的API,兼容不同的版本:
在 Android5.0 (API-21)及以上,getRunningTasks将只返回自己和 launcher,getRunningTasks 无法正确判断当前应用是否为front, 不同版本测试的结果也不一样:
在 Android 5.0版本:
在 Android 5.1, 6.0 以上版本:
对于
getRunningAppProcesses,
经过我自己的测试:
3. 代码示例:
4. 添加 GET_TASKS 权限到
|
不要忘记添加permission 到Manifest 文件:
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />