Android: Comment puis-je obtenir l'activité de premier plan actuelle (à partir d'un service)?

Existe-t-il un moyen natif android pour obtenir une référence à L'activité en cours d'exécution à partir d'un service?

j'ai un service en cours d'exécution sur le fond, et je voudrais mettre à jour mon activité actuelle quand un événement se produit (dans le service). Est-il un moyen facile de le faire (comme je l'ai suggéré ci-dessus)?

158
demandé sur George 2010-10-06 18:39:10

11 réponses

y a-t-il un moyen natif android pour obtenir une référence à L'activité en cours d'exécution à partir d'un service?

vous ne pouvez pas posséder l' "activité en cours d'exécution".

j'ai un service en cours d'exécution sur le fond, et je voudrais mettre à jour mon activité actuelle quand un événement se produit (dans le service). Est-il un moyen facile de le faire (comme je l'ai suggéré ci-dessus)?

  1. envoyer une émission Intent à l'activité -- voici un exemple de projet démontrant ce modèle
  2. avoir l'activité de fournir un PendingIntent (par exemple, via createPendingResult() ) que le service invoque
  3. ont l'activité enregistrer un objet callback ou listener avec le service via bindService() , et avoir le service appeler une méthode d'événement sur cet objet callback /listener
  4. envoyer une émission ordonnée Intent à l'activité, avec une faible priorité BroadcastReceiver comme sauvegarde (pour soulever un Notification si l'activité n'est pas à l'écran) -- voici un billet de blog avec plus sur ce modèle
80
répondu CommonsWare 2014-02-21 12:31:14

Voici une bonne façon de le faire en utilisant le gestionnaire d'activité. Vous obtenez essentiellement les tâches de fonctionnement du gestionnaire d'activités. Il retournera toujours la tâche active en premier. De là, vous pouvez obtenir la topactivité.

exemple ici

il y a un moyen facile d'obtenir une liste des tâches d'exécution du service ActivityManager. Vous pouvez demander un nombre maximum de tâches exécutées sur le téléphone, et par défaut, le actuellement la tâche active est retournée en premier.

une fois que vous avez que vous pouvez obtenir un objet ComponentName en demandant la topactivité de votre liste.

voici un exemple.

    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
    Log.d("topActivity", "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName());
    ComponentName componentInfo = taskInfo.get(0).topActivity;
    componentInfo.getPackageName();

vous aurez besoin de la permission suivante sur votre manifeste:

<uses-permission android:name="android.permission.GET_TASKS"/>
134
répondu Nelson Ramirez 2014-11-25 14:49:08

avertissement: Violation de Google Play

Google a menacé de supprimer les applications de Play Store si elles utilisent des services d'accessibilité à des fins de non-accessibilité. Cependant, ce serait l'objet d'un réexamen .


utiliser un AccessibilityService

prestations

  • testé et travailler sur Android 2.2 (API 8) via Android 7.1 (API 25).
  • ne nécessite pas de sondage.
  • ne nécessite pas la permission GET_TASKS .

désavantages

  • chaque utilisateur doit activer le service dans les paramètres D'accessibilité D'Android.
  • le service est toujours en cours.
  • Lorsqu'un utilisateur essaie d'activer la AccessibilityService , ils ne peuvent pas appuyer sur le bouton OK si une application a placé une superposition sur l'écran. Certaines applications qui font cela sont Velis Auto Brightness et Lux. Cela peut être déroutant parce que l'utilisateur pourrait ne pas savoir pourquoi ils ne peuvent pas appuyer sur le bouton ou la façon de travailler autour de lui.
  • le AccessibilityService ne connaîtra pas l'activité actuelle jusqu'au premier changement de activité.

exemple

Service

public class WindowChangeDetectingService 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(AccessibilityEvent event) {
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            if (event.getPackageName() != null && event.getClassName() != null) {
                ComponentName componentName = new ComponentName(
                    event.getPackageName().toString(),
                    event.getClassName().toString()
                );

                ActivityInfo activityInfo = tryGetActivity(componentName);
                boolean isActivity = activityInfo != null;
                if (isActivity)
                    Log.i("CurrentActivity", componentName.flattenToShortString());
            }
        }
    }

    private ActivityInfo tryGetActivity(ComponentName componentName) {
        try {
            return getPackageManager().getActivityInfo(componentName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
    }

    @Override
    public void onInterrupt() {}
}

AndroidManifest.xml

fusionnez ceci dans votre manifeste:

<application>
    <service
        android:label="@string/accessibility_service_name"
        android:name=".WindowChangeDetectingService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibilityservice"/>
    </service>
</application>

Info Service

Mettre cela en res/xml/accessibilityservice.xml :

<?xml version="1.0" encoding="utf-8"?>
<!-- These options MUST be specified here in order for the events to be received on first
 start in Android 4.1.1 -->
<accessibility-service
    xmlns:tools="http://schemas.android.com/tools"
    android:accessibilityEventTypes="typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagIncludeNotImportantViews"
    android:description="@string/accessibility_service_description"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:ignore="UnusedAttribute"/>

activer le Service

chaque utilisateur de l'application devra explicitement activer le AccessibilityService pour son utilisation. Voir cette réponse à la question pour savoir comment procéder.

notez que l'utilisateur ne pourra pas appuyer sur le bouton OK pour activer le service d'accessibilité si une application a placé une superposition sur l'écran, comme Velis Auto Brightness ou Lux.

96
répondu Sam 2018-01-23 20:34:44

cela peut être fait par:

  1. implémentez votre propre classe d'application, Enregistrez - vous pour ActivityLifecycleCallbacks-de cette façon vous pouvez voir ce qui se passe avec notre application. À chaque reprise le rappel affecte la activité visible sur l'écran et sur pause il supprime l'affectation. Il utilise la méthode registerActivityLifecycleCallbacks() qui a été ajoutée dans L'API 14.

    public class App extends Application {
    
    private Activity activeActivity;
    
    @Override
    public void onCreate() {
        super.onCreate();
        setupActivityListener();
    }
    
    private void setupActivityListener() {
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            }
            @Override
            public void onActivityStarted(Activity activity) {
            }
            @Override
            public void onActivityResumed(Activity activity) {
                activeActivity = activity;
            }
            @Override
            public void onActivityPaused(Activity activity) {
                activeActivity = null;
            }
            @Override
            public void onActivityStopped(Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(Activity activity) {
            }
        });
    }
    
    public Activity getActiveActivity(){
        return activeActivity;
    }
    
    }
    
  2. dans votre appel de service getApplication() et le lancer à votre nom de classe app (App dans ce cas). Que vous pouvez appeler app.getActiveActivity() - qui vous donnera une activité visible courante (ou null quand aucune activité n'est visible). Vous pouvez obtenir le nom de l'Activité en appelant activeActivity.getClass().getSimpleName()

14
répondu Vit Veres 2016-11-27 20:38:05

Utilisation ActivityManager

si vous voulez seulement connaître le application contenant l'activité courante, vous pouvez le faire en utilisant ActivityManager . La technique que vous pouvez utiliser dépend de la version D'Android:

prestations

  • devrait fonctionner dans toutes les versions Android à ce jour.

désavantages

  • Ne fonctionne pas sous Android M (basé sur des tests à l'aide de la pré-version de l'émulateur)
  • la documentation pour ces IPA dit qu'ils sont uniquement destinés au débogage et à la gestion des interfaces utilisateur.
  • si vous voulez des mises à jour en temps réel, vous devez utiliser polling.
  • S'appuie sur une API cachée: ActivityManager.RunningAppProcessInfo.processState
  • cette implémentation ne capte pas l'activité de commutateur d'application.

exemple (basé sur code de KNaito )

public class CurrentApplicationPackageRetriever {

    private final Context context;

    public CurrentApplicationPackageRetriever(Context context) {
        this.context = context;
    }

    public String[] get() {
        if (Build.VERSION.SDK_INT < 21)
            return getPreLollipop();
        else
            return getLollipop();
    }

    private String[] getPreLollipop() {
        @SuppressWarnings("deprecation")
        List<ActivityManager.RunningTaskInfo> tasks =
            activityManager().getRunningTasks(1);
        ActivityManager.RunningTaskInfo currentTask = tasks.get(0);
        ComponentName currentActivity = currentTask.topActivity;
        return new String[] { currentActivity.getPackageName() };
    }

    private String[] getLollipop() {
        final int PROCESS_STATE_TOP = 2;

        try {
            Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");

            List<ActivityManager.RunningAppProcessInfo> processes =
                activityManager().getRunningAppProcesses();
            for (ActivityManager.RunningAppProcessInfo process : processes) {
                if (
                    // Filters out most non-activity processes
                    process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                    &&
                    // Filters out processes that are just being
                    // _used_ by the process with the activity
                    process.importanceReasonCode == 0
                ) {
                    int state = processStateField.getInt(process);

                    if (state == PROCESS_STATE_TOP)
                        /*
                         If multiple candidate processes can get here,
                         it's most likely that apps are being switched.
                         The first one provided by the OS seems to be
                         the one being switched to, so we stop here.
                         */
                        return process.pkgList; 
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return new String[] { };
    }

    private ActivityManager activityManager() {
        return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    }

}

manifeste

ajouter le GET_TASKS permission de AndroidManifest.xml :

<!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />
10
répondu Sam 2017-05-23 11:54:59

Je n'ai pas pu trouver une solution avec laquelle notre équipe serait heureuse alors nous avons roulé notre propre. Nous utilisons ActivityLifecycleCallbacks pour suivre l'activité actuelle et l'exposer ensuite à travers un service:

public interface ContextProvider {
    Context getActivityContext();
}

public class MyApplication extends Application implements ContextProvider {
    private Activity currentActivity;

    @Override
    public Context getActivityContext() {
         return currentActivity;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityStarted(Activity activity) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityResumed(Activity activity) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityPaused(Activity activity) {
                MyApplication.this.currentActivity = null;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                // don't clear current activity because activity may get stopped after
                // the new activity is resumed
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                // don't clear current activity because activity may get destroyed after
                // the new activity is resumed
            }
        });
    }
}

puis configurer votre conteneur DI pour retourner l'instance de MyApplication pour ContextProvider , par exemple

public class ApplicationModule extends AbstractModule {    
    @Provides
    ContextProvider provideMainActivity() {
        return MyApplication.getCurrent();
    }
}

(notez que la mise en œuvre de getCurrent() est omise du code ci-dessus. C'est juste une variable statique qui est définie à partir de la application constructeur)

10
répondu Muxa 2016-07-29 04:50:19

j'utilise ça pour mes tests. C'est API > 19, et seulement pour les activités de votre application, cependant.

@TargetApi(Build.VERSION_CODES.KITKAT)
public static Activity getRunningActivity() {
    try {
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Object activityThread = activityThreadClass.getMethod("currentActivityThread")
                .invoke(null);
        Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
        activitiesField.setAccessible(true);
        ArrayMap activities = (ArrayMap) activitiesField.get(activityThread);
        for (Object activityRecord : activities.values()) {
            Class activityRecordClass = activityRecord.getClass();
            Field pausedField = activityRecordClass.getDeclaredField("paused");
            pausedField.setAccessible(true);
            if (!pausedField.getBoolean(activityRecord)) {
                Field activityField = activityRecordClass.getDeclaredField("activity");
                activityField.setAccessible(true);
                return (Activity) activityField.get(activityRecord);
            }
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    throw new RuntimeException("Didn't find the running activity");
}
9
répondu espinchi 2015-10-04 01:10:04

Voici ma réponse qui fonctionne très bien...

Vous devriez être en mesure d'obtenir l'Activité actuelle de cette façon... Si vous structurez votre application avec quelques activités avec beaucoup de fragments et vous voulez garder une trace de ce qui est votre activité actuelle, il faudrait beaucoup de travail cependant. Mon senario était que j'avais une activité avec plusieurs Fragments. Donc je peux garder une trace de L'activité actuelle à travers L'Application Object, qui peut stocker tous les états actuels des variables globales.

Voici un. Lorsque vous commencez votre activité, vous stockez cette activité par Application.setCurrentActivity(getIntent)()); Cette Application va le stocker. Sur votre classe de service, vous pouvez simplement faire comme Intention actuelle = Application.obteniractivité actuelle(); getApplication().activité de départ (actuelle);

0
répondu John3328 2015-03-26 18:31:58

utilisez ce code pour API 21 ou plus. Cela fonctionne et donne un meilleur résultat par rapport aux autres réponses, il détecte parfaitement le processus de premier plan.

if (Build.VERSION.SDK_INT >= 21) {
    String currentApp = null;
    UsageStatsManager usm = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
    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();
        }
    }
0
répondu Manish Godhani 2016-07-30 12:28:00

Je ne sais pas si c'est une réponse stupide, mais j'ai résolu ce problème en stockant un drapeau dans les préférences partagées chaque fois que j'entrais dans onCreate() de n'importe quelle activité, puis j'ai utilisé la valeur de shered preferences pour trouver ce que c'est l'activité de premier plan.

0
répondu Vlad Vladescu 2017-03-08 03:13:08

vient de l'apprendre. Avec les api comme:

  • minSdkVersion 19
  • targetSdkVersion 26

    ActivityManager.getactiviteencours(contexte)

l'Espoir c'est de toute utilisation.

-1
répondu user3709410 2018-03-14 20:52:51