【问题标题】:getRunningTasks doesn't work in Android LgetRunningTasks 在 Android L 中不起作用
【发布时间】:2014-07-08 07:23:19
【问题描述】:

在 Android L 中,Google 已禁用 getRunningTasks。现在它只能返回自己的应用程序任务和主页启动器。我无法再获得其他应用程序任务。 我们的应用需要该方法来确定当前的热门应用。 任何人有另一种方法来做到这一点?

我在 Google 上搜索过,除此之外没有更多关于此的主题: https://code.google.com/p/android-developer-preview/issues/detail?id=29

【问题讨论】:

标签: android android-5.0-lollipop


【解决方案1】:

这是在您的 Android L/Lollipop 设备和 Android M/Marshmallow 设备上获取当前热门活动的准确解决方案。

首先调用这行代码:(一次)

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);

上面的代码将打开一个名为“具有使用权限的应用程序”的屏幕。只需将单选按钮选中为 on/true 以允许使用访问。 现在在您的服务或您想要的任何地方调用以下方法:

public void getTopActivtyFromLolipopOnwards() {
    String topPackageName;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
        long time = System.currentTimeMillis();
        // We get usage stats for the last 10 seconds
        List < UsageStats > stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);
        // Sort the stats by the last time used
        if (stats != null) {
            SortedMap < Long, UsageStats > mySortedMap = new TreeMap < Long, UsageStats > ();
            for (UsageStats usageStats: stats) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
                Log.e("TopPackage Name", topPackageName);
            }
        }
    }
}

添加权限

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
     tools:ignore="ProtectedPermissions" />

这将返回当前正在运行的活动的包名,无论是facebook还是whatsapp。

此方法的唯一复杂之处是您需要提示用户允许应用使用情况统计...即第一步。

希望!这对每个人都有帮助。

【讨论】:

  • 感谢 'tools:ignore="ProtectedPermissions"'。
  • 对于那些搜索顶级活动名称的人,这将返回顶级活动的包名称,而不是顶级活动的名称
  • PACKAGE_USAGE_STATS 权限发生错误,“权限仅授予系统应用”
  • 工作正常,但是有没有办法过滤通知,这意味着,当通知出现时,此代码显示通知也作为前台进程...我只想获取前台活动..
  • 有效,但目前有没有办法在不提示用户的情况下做同样的事情?
【解决方案2】:

正如 MKY 提到的,getRunningTasks() 方法不适用于获取 Lollipop 中的当前应用程序。

正如 sunxin8086 所写,获取正在运行的应用程序的一种方法是使用getRunningAppsProcesses() 方法。但是条件info.importance == IMPORTANCE_FOREGROUND不能唯一确定当前应用。

确定当前前台应用程序的更好方法可能是检查RunningAppProcessInfo 对象中的processState 字段。该字段是隐藏字段,但您可以在RunningAppProcessInfo 类中看到它。如果这个值为ActivityManager.PROCESS_STATE_TOP(这也是 hidden static constant),进程为当前前台进程。

例如代码是

final int PROCESS_STATE_TOP = 2;
ActivityManager.RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
    field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) {
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo app : appList) {
    if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND 
            && app.importanceReasonCode == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
        Integer state = null;
        try {
            state = field.getInt(app);
        } catch (Exception e) {
        }
        if (state != null && state == PROCESS_STATE_TOP) {
            currentInfo = app;
            break;
        }
    }
}
return currentInfo;

注意: processState 字段在前 Lolipop 中不存在。请在运行上述代码之前检查Build.VERSION.SDK_INT &gt;= 21。以上代码仅适用于 Lollipop+。

Gaston 的另一种方法(完全不同),“当前应用程序”的含义与这种方法略有不同。

请根据您的目的选择一个。


[编辑]

正如 Sam 指出的,我将 START_TASK_TO_FRONT 修改为 PROCESS_STATE_TOP。 (两个值都是 2)

[编辑2]

山姆有了新发现!要唯一确定前台应用程序,还需要一个 条件

process.importanceReasonCode == 0

是必要的。上面的代码已经更新。谢谢!

【讨论】:

  • 我在 Android Studio Nexus 5 Emulator 上进行了测试,似乎可以正常工作,还需要在物理设备上进行实际测试。
  • 在 Nexus 6 上运行良好。
  • 我发现您的代码还返回多个包:包含活动的进程和该进程使用的进程。一个例子是 Firefox 应用程序,它似乎使用了 Google GMS 和 YouTube 进程。为了解决这个问题,我在if 语句中添加了以下检查:process.importanceReasonCode == 0
  • 很棒的发现。谢谢。需要指出的是,您应该使用 app.pkgList[0] 来获取当前正在运行的应用程序的包,而不是 app.processName。大多数情况下它们是相同的,但有时会不同,例如在运行 TuneIn Pro 应用程序时。
  • @KNaito 不幸的是,这在 Android M 上不再起作用。am.getRunningAppProcesses() 仅返回您的应用程序包。
【解决方案3】:
private String getProcess() throws Exception {
    if (Build.VERSION.SDK_INT >= 21) {
        return getProcessNew();
    } else {
        return getProcessOld();
    }
}

//API 21 and above
private String getProcessNew() throws Exception {
    String topPackageName = null;
    UsageStatsManager usage = (UsageStatsManager) context.getSystemService(Constant.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> stats = usage.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - ONE_SECOND * 10, time);
    if (stats != null) {
        SortedMap<Long, UsageStats> runningTask = new TreeMap<Long,UsageStats>();
        for (UsageStats usageStats : stats) {
            runningTask.put(usageStats.getLastTimeUsed(), usageStats);
        }
        if (runningTask.isEmpty()) {
            return null;
        }
        topPackageName =  runningTask.get(runningTask.lastKey()).getPackageName();
    }
    return topPackageName;
}

//API below 21
@SuppressWarnings("deprecation")
private String getProcessOld() throws Exception {
    String topPackageName = null;
    ActivityManager activity = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> runningTask = activity.getRunningTasks(1);
    if (runningTask != null) {
        RunningTaskInfo taskTop = runningTask.get(0);
        ComponentName componentTop = taskTop.topActivity;
        topPackageName = componentTop.getPackageName();
    }
    return topPackageName;
}

//required permissions
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.GET_TASKS"/>

【讨论】:

  • 请提供一些详细信息
  • 无需粘贴此代码并执行 getProcess() 即可获得当前可见的进程
  • Constant.USAGE_STATS_SERVICE 的价值是什么。并且此代码将在后台服务中运行。
  • 相信你可以直接使用Context.USAGE_STATS_SERVICE,即:context.getSystemService(Context.USAGE_STATS_SERVICE);
  • 不工作。我在 Android 5.1.1 中测试了 runningTask.isEmpty() 始终为 true 并返回 null。
【解决方案4】:

我认为不可能获得其他应用的任务,

这就是文档所说的

随着新的并发文档和活动的引入 即将发布的版本中的任务功能(请参阅Concurrent documents and activities in Recents screen below), ActivityManager.getRecentTasks() 方法现在已被弃用以改进 用户隐私。为了向后兼容,这个方法仍然返回一个 其数据的一小部分,包括调用应用程序自己的 任务和可能的其他一些非敏感任务(例如家庭)。如果 您的应用正在使用此方法来检索自己的任务,请使用 android.app.ActivityManager.getAppTasks() 而不是检索那个 信息。

在这里查看Android L的api概述https://developer.android.com/preview/api-overview.html#Behaviors

【讨论】:

    【解决方案5】:

    对于我最近参与的一个项目,我还需要检测某些应用程序何时启动。我所有的研究都导致了 getRunningTasks 方法,该方法从 Lollipop 开始被弃用。 然而,令我惊讶的是,我发现一些应用程序锁定应用程序仍然可以在棒棒糖设备上运行,所以他们一定想出了一个解决方案来解决这个问题。所以我挖得更深一些。这是我发现的:

      1. 在 L 之前的设备上,它们仍然使用 getRunningTasks
      1. 在 L 设备上,它们使用 getRunningAppProcesses,它返回当前在设备上运行的进程列表。你可能会想“好吧,那没用”。每个 processInfo 都有一个称为重要性的属性。当应用成为顶级活动时,其 processInfo 重要性也会更改为 IMPORTANCE_FOREGROUND。所以你可以过滤掉那些不在前台的进程。从每个 ProcessInfo 中,您还可以询问它加载的包的列表。然后,您可以检查列表是否包含与应用程序尝试“受保护”时相同的包。

    一些用于检测默认日历应用何时启动的示例代码:

    public class DetectCalendarLaunchRunnable implements Runnable {
    
    @Override
    public void run() {
      String[] activePackages;
      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
        activePackages = getActivePackages();
      } else {
        activePackages = getActivePackagesCompat();
      }
      if (activePackages != null) {
        for (String activePackage : activePackages) {
          if (activePackage.equals("com.google.android.calendar")) {
            //Calendar app is launched, do something
          }
        }
      }
      mHandler.postDelayed(this, 1000);
    }
    
    String[] getActivePackagesCompat() {
      final List<ActivityManager.RunningTaskInfo> taskInfo = mActivityManager.getRunningTasks(1);
      final ComponentName componentName = taskInfo.get(0).topActivity;
      final String[] activePackages = new String[1];
      activePackages[0] = componentName.getPackageName();
      return activePackages;
    }
    
    String[] getActivePackages() {
      final Set<String> activePackages = new HashSet<String>();
      final List<ActivityManager.RunningAppProcessInfo> processInfos = mActivityManager.getRunningAppProcesses();
      for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
        if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
          activePackages.addAll(Arrays.asList(processInfo.pkgList));
        }
      }
      return activePackages.toArray(new String[activePackages.size()]);
    }
    }
    

    注意:getRunningAppProcesses 也用于调试或“构建面向用户的流程管理 UI”。不确定谷歌是否会以与 getRunningTasks 类似的方式关闭此后门。

    所以不,你不能再获得 topActivity 了。但只要稍加修改,您就可以获得类似的结果。

    【讨论】:

    • 我不明白这如何为您提供顶级活动。它使用 IMPORTANCE_FOREGROUND 返回多个进程,因此无法知道哪个进程在最前面。此外,它在每个进程中返回多个包,其中任何一个都可能位于顶部。那么,我错过了什么吗?
    • 它没有获得top Activity,但是我上面给出的示例,您可以检测到某个应用程序何时启动,这对于很多用例来说已经足够了。
    • 帮助很大。将您和 KNaito 的答案结合在一起并且工作正常。完美运行。
    • 我发现您的代码还返回多个包:包含活动的进程和该进程使用的进程。一个例子是 Firefox 应用程序,它似乎使用了 Google GMS 和 YouTube 进程。为了解决这个问题,我在if 语句中添加了以下检查:process.importanceReasonCode == 0
    • @sunxin8086 不幸的是,这在 Android M 上不再起作用。am.getRunningAppProcesses() 仅返回您的应用程序包。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    相关资源
    最近更新 更多