webentwicklung-frage-antwort-db.com.de

Wie erhalte ich den aktuellen Vordergrundaktivitätskontext in Android?

Immer wenn meine Sendung ausgeführt wird, möchte ich die Aktivität des Vordergrunds anzeigen.

133
Deepali

Wenn wir wissen, dass ActivityManagerActivity verwaltet, können wir Informationen von ActivityManager erhalten. Wir bekommen die aktuelle Vordergrundaktivität von

ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;

UPDATE 2018/10/03
getRunningTasks () ist DEPRECATED. Siehe die Lösungen unten.

Diese Methode wurde in API-Ebene 21 nicht mehr empfohlen. Ab Build.VERSION_CODES.Lollipop steht diese Methode nicht mehr für Anwendungen von Drittanbietern zur Verfügung: Durch die Einführung von dokumentenzentrierten Nachrichten können Personeninformationen an den Aufrufer weitergegeben werden. Aus Gründen der Abwärtskompatibilität wird immer noch eine kleine Teilmenge seiner Daten zurückgegeben: Zumindest die eigenen Aufgaben des Anrufers und möglicherweise einige andere Aufgaben, wie z. B. Privatanwender, von denen bekannt ist, dass sie nicht sensibel sind.

31
KAlO2

(Anmerkung: In API 14 wurde eine offizielle API hinzugefügt: Siehe diese Antwort https://stackoverflow.com/a/29786451/119733 )

VERWENDEN SIE KEINE VORHERIGE (waqas716) Antwort.

Aufgrund des statischen Verweises auf die Aktivität treten Probleme mit dem Speicherverlust auf. Weitere Informationen finden Sie unter folgendem Link http://Android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html

Um dies zu vermeiden, sollten Sie Aktivitätsreferenzen verwalten. Fügen Sie den Namen der Anwendung in der Manifestdatei hinzu:

<application
    Android:name=".MyApp"
    ....
 </application>

Ihre Bewerbungsklasse:

  public class MyApp extends Application {
        public void onCreate() {
              super.onCreate();
        }

        private Activity mCurrentActivity = null;
        public Activity getCurrentActivity(){
              return mCurrentActivity;
        }
        public void setCurrentActivity(Activity mCurrentActivity){
              this.mCurrentActivity = mCurrentActivity;
        }
  }

Erstellen Sie eine neue Aktivität:

public class MyBaseActivity extends Activity {
    protected MyApp mMyApp;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMyApp = (MyApp)this.getApplicationContext();
    }
    protected void onResume() {
        super.onResume();
        mMyApp.setCurrentActivity(this);
    }
    protected void onPause() {
        clearReferences();
        super.onPause();
    }
    protected void onDestroy() {        
        clearReferences();
        super.onDestroy();
    }

    private void clearReferences(){
        Activity currActivity = mMyApp.getCurrentActivity();
        if (this.equals(currActivity))
            mMyApp.setCurrentActivity(null);
    }
}

Anstatt also die Aktivitätsklasse für Ihre Aktivitäten zu erweitern, erweitern Sie einfach MyBaseActivity. Jetzt können Sie Ihre aktuelle Aktivität aus dem Anwendungs- oder Aktivitätskontext wie folgt abrufen:

Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
185
gezdy

Ich möchte auf die Antwort von @ gezdy eingehen.

Anstatt sich bei Application mit manueller Codierung "registrieren" zu müssen, können wir die folgende API seit Level 14 verwenden, um einen ähnlichen Zweck mit weniger manueller Codierung zu erreichen.

public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)

http://developer.Android.com/reference/Android/app/Application.html#registerActivityLifecycleCallbacks%28Android.app.Application.ActivityLifecycleCallbacks%29

In Application.ActivityLifecycleCallbacks können Sie ermitteln, welche Activity an diese Application "angehängt" oder "getrennt" ist.

Diese Technik ist jedoch erst seit API-Level 14 verfügbar.

57
Cheok Yan Cheng

Update 2: Es wurde eine offizielle API hinzugefügt, verwenden Sie stattdessen ActivityLifecycleCallbacks .

UPDATE:

Wie @gezdy darauf hinweist, und dafür bin ich dankbar. setze den Verweis auf null zu für die aktuelle Aktivität, statt nur bei onResume zu aktualisieren, setzen Sie ihn bei onDestroy jeder Aktivität auf null, um Probleme mit dem Speicherverlust zu vermeiden.

Vor einiger Zeit brauchte ich die gleiche Funktionalität und hier ist die Methode, wie ich dies erreicht habe ... In jeder dieser Aktivitäten überschreiben Sie diese Lebenszyklusmethoden.

@Override
protected void onResume() {
    super.onResume();
    appConstantsObj.setCurrentActivity(this);

}

@Override
protected void onPause() {
   clearReferences();
   super.onPause();
}

@Override
protected void onDestroy() {        
   clearReferences();
   super.onDestroy();
}

private void clearReferences(){
          Activity currActivity = appConstantsObj.getCurrentActivity();
          if (this.equals(currActivity))
                appConstantsObj.setCurrentActivity(null);
}

Jetzt können Sie in Ihrer Broadcast-Klasse auf die aktuellen Aktivitäten zugreifen, um darauf aufmerksam zu machen.

50
Waqas

@lockwobr Danke für das Update

Dies funktioniert in API-Version 16 nicht 100% der Zeit, wenn Sie die .__-Datei lesen. Code auf github wurde die Funktion "currentActivityThread" in .__ geändert. KitKat, also ich möchte Version 19ish sagen, irgendwie schwer zu api passen Version zu Releases in Github.

Der Zugriff auf das aktuelle Activity ist sehr praktisch. Wäre es nicht schön, eine statische getActivity-Methode zu haben, die die aktuelle Aktivität ohne unnötige Fragen zurückgibt?

Die Activity-Klasse ist sehr nützlich. Es gibt Zugriff auf den UI-Thread der Anwendung, Ansichten, Ressourcen und vieles mehr. Zahlreiche Methoden erfordern ein Context, aber wie erhält man den Zeiger? Hier sind einige Möglichkeiten:

  • Verfolgen des Anwendungsstatus mithilfe überschriebener Lebenszyklusmethoden . Sie müssen die aktuelle Aktivität in einer statischen Variablen speichern, und Sie müssen auf den Code aller Aktivitäten zugreifen.
  • Verfolgen des Anwendungsstatus mithilfe von Instrumentation. Deklarieren Sie Instrumentation im Manifest, implementieren Sie es und verwenden Sie seine Methoden, um Aktivitätsänderungen nachzuverfolgen. Übergeben eines Aktivitätszeigers an Methoden und Klassen, die in Ihren Aktivitäten verwendet werden. Injektion des Zeigers mit einer der Code-Injektionsbibliotheken. Alle diese Ansätze sind eher Unbequem; Zum Glück gibt es einen viel einfacheren Weg, um die aktuelle Aktivität abzurufen.
  • Anscheinend benötigt das System Zugriff auf alle Aktivitäten ohne die oben erwähnten Probleme. Am wahrscheinlichsten gibt es eine Möglichkeit, __. Aktivitäten nur mit statischen Aufrufen zu erhalten. Ich habe viel Zeit damit verbracht, .__ durch die Android-Quellen auf grepcode.com zu graben, und ich habe gefunden, wonach ich gesucht habe Es gibt eine Klasse namens ActivityThread. Diese Klasse hat Zugriff auf alle Aktivitäten und, was noch besser ist, eine statische Methode Zum Abrufen der aktuellen ActivityThread. Es gibt nur ein kleines Problem - die Aktivitätsliste hat Paketzugriff. 

Mit Reflektion leicht zu lösen:

public static Activity getActivity() {
    Class activityThreadClass = Class.forName("Android.app.ActivityThread");
    Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
    Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
    activitiesField.setAccessible(true);

    Map<Object, Object> activities = (Map<Object, Object>) activitiesField.get(activityThread);
    if (activities == null)
        return null;

    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);
            Activity activity = (Activity) activityField.get(activityRecord);
            return activity;
        }
    }

    return null;
}

Eine solche Methode kann überall in der App verwendet werden und ist viel praktischer als alle genannten Ansätze. Außerdem scheint es nicht so unsicher zu sein, wie es aussieht. Es werden keine neuen potenziellen Lecks oder Nullzeiger eingeführt.

Das obige Code-Snippet enthält keine Ausnahmebehandlung und geht naiv davon aus, dass die zuerst ausgeführte Aktivität die ist, nach der wir suchen. Möglicherweise möchten Sie einige zusätzliche Prüfungen hinzufügen.

Blogeintrag

43
AZ_

getCurrentActivity () befindet sich auch in ReactContextBaseJavaModule.
(Da diese Frage ursprünglich gestellt wurde, enthält viele Android-Apps auch die ReactNative-Komponente - Hybrid-App.)

die Klasse ReactContext in ReactNative enthält die gesamte Logik, um mCurrentActivity aufrechtzuerhalten.

Hinweis: Ich möchte, dass getCurrentActivity () in der Android-Anwendungsklasse implementiert ist.

5
朱梅寧

Ich konnte keine Lösung finden, mit der unser Team zufrieden wäre, also haben wir unsere eigene gewürfelt. Wir verwenden ActivityLifecycleCallbacks, um die aktuellen Aktivitäten zu verfolgen und sie dann über einen Dienst verfügbar zu machen. Weitere Informationen finden Sie hier: https://stackoverflow.com/a/38650587/10793

3
Muxa

Ich persönlich habe das getan, was "Cheok Yan Cheng" gesagt hatte, aber ich verwendete eine "Liste", um einen "Backstack" aller Aktivitäten zu haben.

Wenn Sie prüfen möchten, welche Aktivität die aktuelle Aktivität ist, benötigen Sie nur die letzte Aktivitätsklasse in der Liste.

Erstellen Sie eine Anwendung, die "Anwendung" erweitert, und tun Sie dies:

public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks,
EndSyncReceiver.IEndSyncCallback {

private List<Class> mActivitiesBackStack;
private EndSyncReceiver mReceiver;
    private Merlin mMerlin;
    private boolean isMerlinBound;
    private boolean isReceiverRegistered;

@Override
    public void onCreate() {
        super.onCreate();
        [....]
RealmHelper.initInstance();
        initMyMerlin();
        bindMerlin();
        initEndSyncReceiver();
        mActivitiesBackStack = new ArrayList<>();
    }

/* START Override ActivityLifecycleCallbacks Methods */
    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
        mActivitiesBackStack.add(activity.getClass());
    }

    @Override
    public void onActivityStarted(Activity activity) {
        if(!isMerlinBound){
            bindMerlin();
        }
        if(!isReceiverRegistered){
            registerEndSyncReceiver();
        }
    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {
        if(!AppUtils.isAppOnForeground(this)){
            if(isMerlinBound) {
                unbindMerlin();
            }
            if(isReceiverRegistered){
                unregisterReceiver(mReceiver);
            }
            if(RealmHelper.getInstance() != null){
                RealmHelper.getInstance().close();
                RealmHelper.getInstance().logRealmInstanceCount("AppInBackground");
                RealmHelper.setMyInstance(null);
            }
        }
    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        if(mActivitiesBackStack.contains(activity.getClass())){
            mActivitiesBackStack.remove(activity.getClass());
        }
    }
    /* END Override ActivityLifecycleCallbacks Methods */

/* START Override IEndSyncCallback Methods */
    @Override
    public void onEndSync(Intent intent) {
        Constants.SyncType syncType = null;
        if(intent.hasExtra(Constants.INTENT_DATA_SYNC_TYPE)){
            syncType = (Constants.SyncType) intent.getSerializableExtra(Constants.INTENT_DATA_SYNC_TYPE);
        }
        if(syncType != null){
            checkSyncType(syncType);
        }
    }
    /* END IEndSyncCallback Methods */

private void checkSyncType(Constants.SyncType){
    [...]
    if( mActivitiesBackStack.contains(ActivityClass.class) ){
         doOperation()     }
}

}

In meinem Fall habe ich "Application.ActivityLifecycleCallbacks" verwendet, um:

  • Merlin-Instanz binden/aufheben (wird verwendet, um ein Ereignis abzurufen, wenn die App die Verbindung verliert oder erhält, z. B. wenn Sie mobile Daten schließen oder öffnen). Dies ist nützlich, nachdem die Absichtsaktion "OnConnectivityChanged" deaktiviert wurde . Weitere Informationen zu MERLIN finden Sie unter: MERLIN INFO LINK

  • Schließen Sie meine letzte Realm-Instanz, wenn die Anwendung geschlossen wird. Ich werde es innerhalb einer BaseActivity initiieren, die von allen anderen Aktivitäten erweitert wird und die eine private RealmHelper-Instanz hat. Weitere Informationen zu REALM finden Sie unter: REALM INFO LINK Zum Beispiel habe ich eine statische "RealmHelper" -Instanz in meiner "RealmHelper" -Klasse, die in meiner Anwendung "onCreate" instanziiert wird. Ich habe einen Synchronisationsdienst, in dem ich einen neuen "RealmHelper" erstelle, weil Realm "Thread-Linked" ist und eine Realm-Instanz nicht in einem anderen Thread arbeiten kann Damit Sie der Realm-Dokumentation folgen können, müssen Sie sie schließen Alle geöffneten Realm-Instanzen zur Vermeidung von Systemressourcenlecks ", um dies zu erreichen, habe ich" Application.ActivityLifecycleCallbacks "verwendet, wie Sie sehen können. 

  • Schließlich habe ich einen Empfänger, der ausgelöst wird, wenn ich meine Anwendung synchronisiert habe. Wenn das Synchronisierungsende beendet ist, ruft er die "IEndSyncCallback" -Methode "onEndSync" auf, in der ich nachschaue, ob ich eine bestimmte Aktivitätsklasse in meiner ActivitiesBackStack-Liste habe, da ich dies brauche um eine Operation auszuführen, wenn ich diesen Punkt in meiner Anwendung bereits überschritten habe.

Das ist alles, hoffe das ist hilfreich. Bis später :)

0
Z3R0

Aus Gründen der Abwärtskompatibilität:

ComponentName cn;
ActivityManager am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if (Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.M) {
    cn = am.getAppTasks().get(0).getTaskInfo().topActivity;
} else {
    //noinspection deprecation
    cn = am.getRunningTasks(1).get(0).topActivity;
}
0
Martin Zeitler

Ich habe die folgenden in Kotlin gemacht

  1. Anwendungsklasse erstellen
  2. Bearbeiten Sie die Anwendungsklasse wie folgt

    class FTApplication: MultiDexApplication() {
    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        MultiDex.install(this)
    }
    
    init {
        instance = this
    }
    
    val mFTActivityLifecycleCallbacks = FTActivityLifecycleCallbacks()
    
    override fun onCreate() {
        super.onCreate()
    
        registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
    }
    
    companion object {
        private var instance: FTApplication? = null
    
        fun currentActivity(): Activity? {
    
            return instance!!.mFTActivityLifecycleCallbacks.currentActivity
        }
    }
    
     }
    
  3. Erstellen Sie die ActivityLifecycleCallbacks-Klasse 

    class FTActivityLifecycleCallbacks: Application.ActivityLifecycleCallbacks {
    
    var currentActivity: Activity? = null
    
    override fun onActivityPaused(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityResumed(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityStarted(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityDestroyed(activity: Activity?) {
    }
    
    override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
    }
    
    override fun onActivityStopped(activity: Activity?) {
    }
    
    override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
        currentActivity = activity
    }
    
    }
    
  4. sie können es jetzt in jeder Klasse verwenden, indem Sie Folgendes aufrufen: FTApplication.currentActivity()

0
rashad.z