「Android」在后台服务中获取前台任务包名
@wenmingvs 总结了在 Android 里判断 APP 是否处于前台的方法,并把它们整合成了一个工具库 AndroidProcess。
方法 | 判断原理 | 需要权限 | 特点 |
---|---|---|---|
1 | 否 | Android 5.0 以上方法被废弃 | |
2 | 否 | Android 5.1 以上只能获取本进程 | |
3 | UsageStatsService | 是 | 最符合Google规范 |
4 | 通过Android无障碍功能实现 | 是 | 需要用户手动授权 |
5 | 是 | IO操作会引起耗时且Android系统限制 |
经过若干验证,以上方法大多都无法(或存在较多限制)在后台服务中获取前台任务包名。综合比较而言,方法3中通过 UsageStatsManager 类来实现前台任务包名的获取可行性较高。
UsageStatsManager
UsageStatsManager 类为使用情况统计管理者,该类提供了设备使用历史和统计信息,具体数据按天、周、月、年等时间间隔进行聚合。
首先需要在AndroidManifest中声明对应权限:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
以下方法为通过 UsageStatsManager 类来获取前台任务包名的具体实现:
/**
* 获取前台应用包名
*
* @param context 上下文
* @return 前台应用包名
*/
public static synchronized String getForegroundPackageName(Context context) {
Log.info(true, TAG, "getForegroundPackageName");
String pkgName = Constants.EMPTY_STRING;
if (!checkUsageAccessPermission(context)) {
// 引导用户打开权限
Log.info(true, TAG, "getForegroundPackageName: no usage access permission");
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.setData(Uri.parse("package:" + context.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return Constants.EMPTY_STRING;
}
long time = System.currentTimeMillis();
long duration = NUMBER_TEN * ONE_MINUTE;
long beginTime = time - duration;
long endTime = time;
Object systemService = context.getSystemService(Context.USAGE_STATS_SERVICE);
if (systemService == null) {
return Constants.EMPTY_STRING;
}
if (!(systemService instanceof UsageStatsManager)) {
return Constants.EMPTY_STRING;
}
UsageStatsManager usageStatsManager = (UsageStatsManager) systemService;
UsageEvents usageEvents = usageStatsManager.queryEvents(beginTime, endTime);
UsageEvents.Event event = new UsageEvents.Event();
while (usageEvents.hasNextEvent()) {
usageEvents.getNextEvent(event);
if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
if (TextUtils.equals(context.getPackageName(), event.getPackageName())) {
continue;
}
pkgName = event.getPackageName();
}
}
Log.info(true, TAG, "pkgName: ", pkgName);
return pkgName;
}
/**
* 判断是否具有用户使用情况权限
*
* @param context 上下文
* @return 是否具有用户使用情况权限
*/
private static boolean checkUsageAccessPermission(Context context) {
Log.info(true, TAG, "checkUsageAccessPermission");
Object systemService = context.getSystemService(Context.USAGE_STATS_SERVICE);
if (systemService == null) {
return false;
}
if (!(systemService instanceof UsageStatsManager)) {
return false;
}
UsageStatsManager usageStatsManager = (UsageStatsManager) systemService;
long time = System.currentTimeMillis();
long duration = NUMBER_TEN * ONE_MINUTE;
long beginTime = time - duration;
long endTime = time;
List<UsageStats> usageStatsList =
usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, beginTime, endTime);
return usageStatsList != null && usageStatsList.size() != Constants.DEFAULT_INT_ZERO;
}
参考
https://effmx.com/articles/to...
https://github.com/wenmingvs/...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。