Android 5.1.1 e versioni successive: getRunningAppProcesses () restituisce solo il pacchetto dell'applicazione


100

Sembra che Google abbia finalmente chiuso tutte le porte per ottenere l'attuale pacchetto di applicazioni in primo piano.

Dopo l'aggiornamento di Lollipop, che ha ucciso getRunningTasks(int maxNum)e grazie a questa risposta , ho usato questo codice per ottenere il pacchetto dell'applicazione in primo piano da 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;

Sembra getRunningAppProcesses()che anche Android 5.1.1 e versioni successive (6.0 Marshmallow) siano stati uccisi . Ora restituisce un elenco del pacchetto dell'applicazione.


UsageStatsManager

Possiamo usare la nuova UsageStatsManagerAPI come descritto qui ma non funziona per tutte le applicazioni. Alcune applicazioni di sistema restituiranno lo stesso pacchetto

com.google.android.googlequicksearchbox

AccessibilityService (dicembre 2017: verrà vietato l'uso da parte di Google)

Alcune applicazioni usano AccessibilityService(come visto qui ) ma presenta alcuni svantaggi.


C'è un altro modo per ottenere il pacchetto dell'applicazione corrente in esecuzione?


1
Du Speed ​​Booster sembra funzionare. Non sono sicuro che utilizzi UsageStatsManager.
thecr0w

3
Tieni presente che diversi importanti fornitori hanno rimosso l'attività di sistema che concede l'accesso all'API UsageStats dai loro dispositivi. Ciò significa che le app su quei dispositivi non potranno mai acquisire l'autorizzazione necessaria.
Kevin Krumwiede

3
Samsung è uno di loro. È oltre il 60% del mercato.
Kevin Krumwiede,

3
Ecco un ticket del bug tracker di Android relativo a questo problema: code.google.com/p/android-developer-preview/issues/…
Florian Barth

2
Se analizzo l'output dell'esecuzione psin una shell e la politica è "fg"e il contenuto di è /proc/[pid]/oom_adj_scoreuguale, 0l'app è l'applicazione in primo piano. Sfortunatamente, sembra /proc/[pid]/oom_adj_scorenon sia più leggibile su Android 6.0. gist.github.com/jaredrummler/7d1498485e584c8a120e
Jared Rummler

Risposte:


93

Per ottenere un elenco dei processi in esecuzione su Android 1.6 - Android 6.0 è possibile utilizzare questa libreria che ho scritto: https://github.com/jaredrummler/AndroidProcesses La libreria legge / proc per ottenere informazioni sul processo.

Google ha limitato in modo significativo l'accesso a / proc in Android Nougat. Per ottenere un elenco dei processi in esecuzione su Android Nougat dovrai utilizzare UsageStatsManager o avere accesso root.

Fare clic sulla cronologia delle modifiche per le soluzioni alternative precedenti.


4
@androiddeveloper no, ho usato solo libsuperuser perché fornisce un modo semplice per eseguire un comando di shell (non root o root) e ottenere l'output. Posso riscriverlo senza libsuperuser se c'è abbastanza richiesta.
Jared Rummler

1
Scusa per quello. Ero solo curioso. :(
sviluppatore Android

1
@JaredRummler Attualmente sto implementando la tua soluzione nella mia app. Sembra funzionare bene per quanto ne so
guy.gc

1
@androiddeveloper, se si desidera ordinare che in base a un attributo diverso, fare Processimplementare Comparablee override del compareTometodo. Quindi, quando lo usi Collections.sort(processesList), userà l'ordine che hai specificato.
Srini

2
@BruceWayne Penso che Jared si riferisca a questo link: 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;
}

Usa questo metodo per ottenere l'attività in primo piano. Avrai bisogno di un'autorizzazione di sistema "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;
    }
}

SE l'utente lo abilita nelle impostazioni -> Sicurezza-> app con accesso all'utilizzo. Dopodiché otterrai l'attività in primo piano. Processo simile Clean matser dal link di Google Play di Cheetahamobile


Come sottolineato nel PO e nei commenti, l'uso di UsageStatsManager. Samsung ha rimosso le richieste ed è piuttosto fastidioso per un utente concedere un'autorizzazione di sistema.
Jared Rummler

Ho testato nel dispositivo Moto E2. Funziona bene e sì, dobbiamo implementare questa autorizzazione. tutti dobbiamo affrontare gli stessi problemi. Abbiamo tutti davvero bisogno di un approccio migliore. Buona fortuna amico
Tarun Sharma

2
Questo approccio non funziona almeno su LG G3 perché non è presente la voce di menu "Impostazioni -> Sicurezza-> App con accesso all'utilizzo"
Dmitry

2
Non è una risposta alla mia domanda. Inoltre, in questa risposta non vengono fornite nuove informazioni.
Lior Iluz

1
Funziona bene ma non correttamente in marshmallow. se la notifica è arrivata, il processo in esecuzione corrente sarà la notifica del pacchetto ricevuta. in realtà l'app non è in esecuzione in primo piano o in background :(. need solution.
WonderSoftwares

6

Dai un'occhiata a https://github.com/ricvalerio/foregroundappchecker , potrebbe essere quello che ti serve. Fornisce codice di esempio e elimina il problema di dover implementare il rilevatore di primo piano versione incrociata.

Ecco due esempi:

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

Oppure controlla regolarmente:

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
Grazie, ma qui non c'è niente di nuovo. Ha appena eseguito il wrapping dell'API UsageStatsManager menzionata in OP. L'utente dovrà abilitare l'accesso, come altri metodi da Android 5.1.1
Lior Iluz

@ Jérémy hai richiesto le autorizzazioni richieste?
rvalerio

Sembra che stia anche facendo sondaggi tutto il tempo, giusto?
sviluppatore Android

4

Google ha limitato questa funzionalità solo alle app di sistema. Come riportato in un bug ticket , avrai bisogno dell'autorizzazione REAL_GET_TASKS per accedervi.

Le applicazioni devono ora avere ... permission.REAL_GET_TASKS per poter ottenere le informazioni di processo per tutte le applicazioni. Se l'app non dispone dell'autorizzazione, verranno restituite solo le informazioni sul processo per l'applicazione chiamante. Le app con privilegi saranno temporaneamente in grado di ottenere informazioni di processo per tutte le applicazioni se non dispongono della nuova autorizzazione, ma hanno deprecato ... permission.GET_TASKS Inoltre, solo le app di sistema possono acquisire l'autorizzazione REAL_GET_TASKS.


Ho visto che applock funziona ancora su 5.1.1, una volta che l'app ha ottenuto l'autorizzazione in sicurezza -> "app con accesso per utilizzo" ... Questo è piuttosto sorprendente
mahesh ren

"Le autorizzazioni con il livello di protezione signature, privileged o signatureOrSystem vengono concesse solo alle app di sistema. Se un'app è una normale app non di sistema, non sarà mai in grado di utilizzare queste autorizzazioni." Afferma Android Studio .. Buona fortuna per negoziare con Google da accettare come "app di sistema".
Jérémy

2

Il solo lancio di una potenziale ottimizzazione per ciò che immagino sia un bit di codice pesantemente incollato per rilevare l'applicazione più in alto su Android M.

Questo

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();
        }
    }
}

Può essere semplificato a questo

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();
    }
}

Mi sono ritrovato a utilizzare questo codice in un ciclo di 2 secondi e mi sono chiesto perché stavo usando una soluzione complessa che era O (n * log (n)) quando una soluzione più semplice era disponibile in Collections.max () che è 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" />

Quindi avvia il servizio e l'accessibilità dell'app nelle impostazioni del tuo dispositivo-> accessibilità-> App su quel servizio.


Questo è già trattato nella domanda e hai appena copiato e incollato il codice dalla sua fonte originale su StackOverflow.
Sam

-2

Si prega di provare a utilizzare al getRunningServices()posto del getRunningAppProcesses()metodo.

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

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

3
Benvenuto in Stack Overflow! Non penso che questo funzionerà poiché l'app in primo piano potrebbe non eseguire un servizio.
Sam
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.