「Android」在后台服务中获取前台任务包名

@wenmingvs 总结了在 Android 里判断 APP 是否处于前台的方法,并把它们整合成了一个工具库 AndroidProcess

方法判断原理需要权限特点
1RunningTaskAndroid 5.0 以上方法被废弃
2RunningProcessAndroid 5.1 以上只能获取本进程
3UsageStatsService最符合Google规范
4通过Android无障碍功能实现需要用户手动授权
5读取/proc目录下的信息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/...


山庄的铁匠
15 声望11 粉丝