Android 5.1.1 et supérieur - getRunningAppProcesses () renvoie uniquement mon package d'application


100

Il semble que Google ait finalement fermé toutes les portes pour obtenir le package d'application actuel au premier plan.

Après la mise à jour de Lollipop, qui a tué getRunningTasks(int maxNum)et grâce à cette réponse , j'ai utilisé ce code pour obtenir le package d'application au premier plan depuis Lollipop:

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

Android 5.1.1 et supérieur (6.0 Marshmallow), semble-t-il, également tué getRunningAppProcesses(). Il renvoie maintenant une liste de votre propre package d'application.


UsageStatsManager

Nous pouvons utiliser la nouvelle UsageStatsManagerAPI comme décrit ici mais cela ne fonctionne pas pour toutes les applications. Certaines applications système renverront le même package

com.google.android.googlequicksearchbox

AccessibilityService (décembre 2017: Google va être interdit d'utilisation par Google)

Certaines applications l'utilisent AccessibilityService(comme vu ici ) mais cela présente quelques inconvénients.


Existe-t-il un autre moyen d'obtenir le package d'application en cours d'exécution?


1
Du Speed ​​Booster semble fonctionner. Je ne sais pas s'il utilise UsageStatsManager.
thecr0w

3
Notez que plusieurs grands fournisseurs ont supprimé l'activité système qui autorise l'accès à l'API UsageStats à partir de leurs appareils. Cela signifie que les applications sur ces appareils ne peuvent jamais acquérir l'autorisation nécessaire.
Kevin Krumwiede

3
Samsung étant l'un d'entre eux. C'est plus de 60% du marché.
Kevin Krumwiede

3
Voici un ticket du traqueur de bogues Android concernant ce problème: code.google.com/p/android-developer-preview/issues/…
Florian Barth

2
Si j'analyse la sortie de l'exécution psdans un shell et que la stratégie est "fg"et le contenu de /proc/[pid]/oom_adj_scoreégal, 0l'application est l'application de premier plan. Malheureusement, il semble /proc/[pid]/oom_adj_scorene plus être lisible sur Android 6.0. gist.github.com/jaredrummler/7d1498485e584c8a120e
Jared Rummler

Réponses:


93

Pour obtenir une liste des processus en cours d'exécution sur Android 1.6 - Android 6.0, vous pouvez utiliser cette bibliothèque que j'ai écrite: https://github.com/jaredrummler/AndroidProcesses La bibliothèque lit / proc pour obtenir des informations sur le processus.

Google a considérablement limité l'accès à / proc dans Android Nougat. Pour obtenir une liste des processus en cours sur Android Nougat, vous devrez utiliser UsageStatsManager ou disposer d'un accès root.

Cliquez sur l'historique des modifications pour les solutions alternatives précédentes.


4
@androiddeveloper non, je n'ai utilisé que libsuperuser car il fournit un moyen facile d'exécuter une commande shell (non root ou root) et d'obtenir la sortie. Je peux le réécrire sans libsuperuser si la demande est suffisante.
Jared Rummler du

1
Désolé. C'était juste curieux. :(
développeur android

1
@JaredRummler Je suis actuellement en train d'implémenter votre solution dans mon application. Semble bien fonctionner pour autant que je
sache

1
@androiddeveloper, si vous souhaitez le trier en fonction d'un attribut différent, faites Processimplémenter Comparableet remplacez la compareTométhode. Ensuite, lorsque vous utilisez Collections.sort(processesList), il utilisera l'ordre que vous avez spécifié.
Srini

2
@BruceWayne Je pense que Jared se réfère à ce lien: https://source.android.com/devices/tech/security/selinux/index.html
Eduardo Herzer

24
 private String printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
        long time = System.currentTimeMillis();
        List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
        if (appList != null && appList.size() > 0) {
            SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
            for (UsageStats usageStats : appList) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e("adapter", "Current App in foreground is: " + currentApp);
    return currentApp;
}

Utilisez cette méthode pour obtenir la tâche de premier plan. Vous aurez besoin d'une autorisation système "android: get_usage_stats"

public static boolean needPermissionForBlocking(Context context){
    try {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
        AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
        return  (mode != AppOpsManager.MODE_ALLOWED);
    } catch (PackageManager.NameNotFoundException e) {
        return true;
    }
}

Si l'utilisateur active cela dans le paramètre -> Sécurité-> application avec accès à l'utilisation. Après cela, vous obtiendrez une tâche au premier plan. Processus similaire Clean matser par Cheetahamobile lien Google Play


Comme indiqué dans le PO et les commentaires, il y a des inconvénients majeurs à l'utilisation UsageStatsManager. Samsung a supprimé les demandes et il est assez ennuyeux pour un utilisateur d'accorder une autorisation système.
Jared Rummler

J'ai testé l'appareil moto e2. Cela fonctionne bien et oui, nous devons implémenter cette autorisation. nous sommes tous confrontés aux mêmes problèmes. Nous avons tous vraiment besoin d'une meilleure approche. Bonne chance mec
Tarun Sharma

2
Cette approche ne fonctionne pas au moins sur LG G3 car il n'y a pas d'élément de menu "Paramètres -> Sécurité-> Application avec accès à l'utilisation"
Dmitry

2
Ce n'est pas une réponse à ma question. De plus, aucune nouvelle information n'est fournie dans cette réponse.
Lior Iluz

1
Fonctionne bien mais pas correctement dans la guimauve. si la notification est arrivée, le processus en cours d'exécution sera la notification de package reçue. en fait, l'application ne fonctionne pas au premier plan ou en arrière-plan: (. besoin d'une solution.
WonderSoftwares

6

Jetez un œil à https://github.com/ricvalerio/foregroundappchecker , c'est peut-être ce dont vous avez besoin. Fournit un exemple de code et évite d'avoir à implémenter un détecteur de premier plan de versions croisées.

Voici deux exemples:

AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();

Ou vérifiez régulièrement:

AppChecker appChecker = new AppChecker();
appChecker
    .when("com.other.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .when("com.my.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .other(new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .timeout(1000)
    .start(this);

2
Merci mais rien de nouveau ici. Il vient d'encapsuler l'API UsageStatsManager qui est mentionnée dans OP. L'utilisateur devra activer l'accès, comme les autres méthodes depuis Android 5.1.1
Lior Iluz

@ Jérémy avez-vous demandé les autorisations requises?
rvalerio

Il semble que ce soit aussi des sondages tout le temps, non?
développeur android

4

Google a limité cette fonctionnalité aux applications système uniquement. Comme signalé dans un ticket de bogue , vous aurez besoin de l' autorisation REAL_GET_TASKS pour y accéder.

Les applications doivent maintenant avoir ... permission.REAL_GET_TASKS pour pouvoir obtenir des informations de processus pour toutes les applications. Seules les informations de processus pour l'application appelante seront renvoyées si l'application ne dispose pas de l'autorisation. Les applications Privilèges pourront temporairement obtenir des informations de processus pour toutes les applications si elles ne disposent pas de la nouvelle autorisation, mais ont une autorisation obsolète ... GET_TASKS De plus, seules les applications système peuvent acquérir l'autorisation REAL_GET_TASKS.


J'ai vu que l'applock fonctionnait toujours sur 5.1.1, une fois que l'application obtenait l'autorisation de sécurité -> "applications avec accès à l'utilisation" ... C'est quelque peu surprenant
mahesh ren

"Les autorisations avec le niveau de protection signature, privilégié ou signatureOrSystem ne sont accordées qu'aux applications système. Si une application est une application non système standard, elle ne pourra jamais utiliser ces autorisations." Déclare android studio. Bonne chance pour négocier avec Google doit être accepté comme "application système".
Jérémy

2

Il suffit de lancer une optimisation potentielle de ce que j'imagine est un morceau de code fortement copié pour détecter l'application la plus performante sur Android M.

Ce

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
    long time = System.currentTimeMillis();
    List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
    if (appList != null && appList.size() > 0) {
        SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
        for (UsageStats usageStats : appList) {
            mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
        }
        if (mySortedMap != null && !mySortedMap.isEmpty()) {
            currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
        }
    }
}

Peut être simplifié à ceci

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
        Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            time - 1000 * 1000, time);
    if (appStatsList != null && !appStatsList.isEmpty()) {
        currentApp = Collections.max(appStatsList, (o1, o2) ->
            Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
    }
}

Je me suis retrouvé à utiliser ce code dans une boucle de 2 secondes et je me suis demandé pourquoi j'utilisais une solution complexe qui était O (n * log (n)) alors qu'une solution plus simple était disponible dans Collections.max () qui est O (n ).


0
public class AccessibilityDetectingService 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(final AccessibilityEvent event) {
        if (event == null ) {
            return;
        } else if(event.getPackageName() == null && event.getClassName() == null){
            return;
        }

            if (activityInfo != null){

                Log.d("CurrentActivity", componentName.flattenToShortString());
        }

}

private ActivityInfo tryGetActivity(ComponentName componentName) {
    try {
        return getPackageManager().getActivityInfo(componentName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
}
@Override
public void onInterrupt() {
}                
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />

Ensuite, démarrez le service et l'accessibilité de l'application dans les paramètres de votre appareil-> accessibilité-> Application sur ce service.


Cela est déjà couvert dans la question et vous venez de copier-coller le code de sa source d'origine sur StackOverflow.
Sam

-2

Veuillez essayer d'utiliser à la getRunningServices()place de la getRunningAppProcesses()méthode.

 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);

3
Bienvenue dans Stack Overflow! Je ne pense pas que cela fonctionnera car l'application au premier plan n'exécute peut-être pas de service.
Sam
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.