getRunningTasks не работает в Android L

В Android L Google отключил getRunningTasks. Теперь он может возвращать только собственные приложения и домашнюю панель запуска. Я больше не могу получить другие задачи приложений. Наше приложение нуждается в этом методе, чтобы определить текущее главное приложение. У кого-нибудь есть другой способ сделать это?

Я искал в Google, больше никаких тем по этому поводу, кроме этой: https://code.google.com/p/android-developer-preview/issues/detail?id=29

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 также предназначен для отладки или "создания пользовательского интерфейса управления процессами". Не уверен, что Google закроет этот бэкдор так же, как они сделали, чтобы получить RunTasks.

Так что нет, вы не можете получить topActivity больше. Но с небольшим количеством взлома вы можете достичь аналогичного результата.

Как упоминал MKY, getRunningTasks() Метод не работает для получения текущего приложения в Lollipop.

Как пишет sunxin8086, одним из способов получения запущенных приложений является использование getRunningAppsProcesses() метод. Тем не менее, условие info.importance == IMPORTANCE_FOREGROUND не может определить текущее приложение однозначно.

Лучшим подходом для определения текущего приложения переднего плана может быть проверка processState поле в RunningAppProcessInfo объект. Это поле скрытое, но вы можете увидеть его в RunningAppProcessInfo учебный класс. Если это значение ActivityManager.PROCESS_STATE_TOP (который также является скрытой статической константой), этот процесс является текущим процессом переднего плана.

Например, код

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 поле не существует в pre-Lolipop. Пожалуйста, проверьте это Build.VERSION.SDK_INT >= 21 перед запуском вышеуказанного кода. Приведенный выше код работает только для Lollipop +.

Другой подход, предложенный Гастоном (который совершенно другой), и значение "текущего приложения" немного отличается от этого подхода.

Пожалуйста, выберите один для вашей цели.


[РЕДАКТИРОВАТЬ]

Как отметил Сэм, я изменил START_TASK_TO_FRONT от PROCESS_STATE_TOP, (Оба значения 2)

[EDIT2]

У Сэма новая находка! Чтобы однозначно определить приложение переднего плана, еще одно условие

process.importanceReasonCode == 0

является необходимым. Приведенный выше код был обновлен. Спасибо!

Вот точное решение для получения максимальной активности на устройствах Android L/Lollipop и Android M/Marshmallow.

Сначала назовите эту строку кода:(Один раз)

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

Приведенный выше код откроет экран под названием "Приложения с доступом". Просто установите переключатель в положение вкл / истина, чтобы разрешить доступ к использованию. Теперь вызовите следующий метод в вашем сервисе или в любом другом месте:

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.

Единственная сложность этого метода в том, что вам нужно запросить у пользователя разрешение на использование статистики приложения... т.е. первый шаг.

Надежда! это помогает всем.

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"/>

Я думаю, что не возможно получить задачи другого приложения,

Это то, что говорит документация

С появлением в следующем выпуске новой функции одновременных документов и задач (см. Ниже раздел "Параллельные документы и действия на экране" Последние "" ) метод ActivityManager.getRecentTasks() устарел для улучшения конфиденциальности пользователей. Для обратной совместимости этот метод по-прежнему возвращает небольшое подмножество своих данных, включая собственные задачи вызывающего приложения и, возможно, некоторые другие не чувствительные задачи (такие как Home). Если ваше приложение использует этот метод для извлечения своих собственных задач, используйте вместо этого android.app.ActivityManager.getAppTasks() для получения этой информации.

Ознакомьтесь с обзором API Android L здесь https://developer.android.com/preview/api-overview.html

Другие вопросы по тегам