it-swarm.com.de

Wie kann man überprüfen, ob sich die Aktivität im Vordergrund oder im sichtbaren Hintergrund befindet?

Ich habe einen Begrüßungsbildschirm für einen Timer. Mein Problem ist, dass ich vor dem finish() meiner Aktivität überprüfen muss, ob die nächste Aktivität gestartet wurde, da ein Systemdialogfeld angezeigt wird und ich nur finish() möchte. Sobald der Benutzer eine Option aus dem Dialogfeld ausgewählt hat? 

Ich weiß, dass es viele Fragen gibt, wie Sie sehen können, ob Ihre Aktivität im Vordergrund steht, aber ich weiß nicht, ob dies Dialogfelder zusätzlich zu der Aktivität ermöglicht. 

Hier ist das Problem, das Rot ist meine Aktivität, die sich im Hintergrund befindet, während der Dialog im Vordergrund steht:

the red is my activity which is in the background while the dialogue is in the foreground

EDIT: Ich habe gerade versucht, finish() nicht zu verwenden, aber dann kann meine Aktivität wieder in den Stapel von Anwendungen verschoben werden, die ich zu vermeiden versuche.

91
Nick

Folgendes wird als right -Lösung empfohlen:

Die richtige Lösung (Credits gehen an Dan, CommonsWare und NeTeInStEiN) Verfolgen Sie die Sichtbarkeit Ihrer Anwendung mithilfe von Activity.onPause, Activity.onResume-Methoden. Speichern Sie den Status "Sichtbarkeit" in einer anderen Klasse. Eine gute Wahl ist Ihre eigene Implementierung von Anwendung oder einen Dienst (es gibt auch einige Variationen dieser -Lösung, wenn Sie die Sichtbarkeit der Aktivitäten des Dienstes prüfen möchten).

Beispiel Implementieren Sie eine benutzerdefinierte Anwendungsklasse (beachten Sie die statische Methode isActivityVisible ()):

public class MyApplication extends Application {

  public static boolean isActivityVisible() {
    return activityVisible;
  }  

  public static void activityResumed() {
    activityVisible = true;
  }

  public static void activityPaused() {
    activityVisible = false;
  }

  private static boolean activityVisible;
}

Registrieren Sie Ihre Anwendungsklasse in AndroidManifest.xml:

<application
    Android:name="your.app.package.MyApplication"
    Android:icon="@drawable/icon"
    Android:label="@string/app_name" >

Fügen Sie zu jeder Aktivität im Projekt onPause und onResume hinzu (wenn Sie möchten, können Sie einen gemeinsamen Vorfahren für Ihre Aktivitäten erstellen. Falls dies jedoch der Fall ist, ist Ihre Aktivität bereits um MapActivity/ListActivity usw. erweitert müssen noch Folgendes von Hand schreiben:

@Override
protected void onResume() {
  super.onResume();
  MyApplication.activityResumed();
}

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

In Ihrer finish()-Methode möchten Sie mit isActivityVisible() prüfen, ob die Aktivität sichtbar ist oder nicht. Dort können Sie auch überprüfen, ob der Benutzer eine Option ausgewählt hat oder nicht. Fahren Sie fort, wenn beide Bedingungen erfüllt sind.

Die Quelle erwähnt auch zwei falsche Lösungen ... also vermeiden Sie dies.

Quelle: stackoverflow

Wenn Sie die API-Ebene 14 oder höher verwenden, können Sie Android.app.Application.ActivityLifecycleCallbacks verwenden. 

public class MyApplication extends Application implements ActivityLifecycleCallbacks {
    private static boolean isInterestingActivityVisible;

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

        // Register to be notified of activity state changes
        registerActivityLifecycleCallbacks(this);
        ....
    }

    public boolean isInterestingActivityVisible() {
        return isInterestingActivityVisible;
    }

    @Override
    public void onActivityResumed(Activity activity) {
        if (activity instanceof MyInterestingActivity) {
             isInterestingActivityVisible = true;
        }
    }

    @Override
    public void onActivityStopped(Activity activity) {
        if (activity instanceof MyInterestingActivity) {
             isInterestingActivityVisible = false;
        }
    }

    // Other state change callback stubs
    ....
}
60

Das ist genau der Unterschied zwischen den onPause- und onStop-Ereignissen der Aktivität, wie in der Dokumentation der Activity-Klasse beschrieben.

Wenn ich Sie richtig verstehe, rufen Sie finish() von Ihrer Aktivität onStop auf, um sie zu beenden. Siehe das beigefügte Bild der Activity Lifecycle Demo App . So sieht es aus, wenn Aktivität B von Aktivität A ... gestartet wird. Die Reihenfolge der Ereignisse ist von unten nach oben, sodass Sie sehen können, dass Aktivität A onStop aufgerufen wird, nachdem Aktivität B onResume bereits aufgerufen wurde.

Activity lifecycle demo

Wenn ein Dialog angezeigt wird, ist Ihre Aktivität im Hintergrund abgeblendet und es wird nur onPause aufgerufen.

10
Muzikant

Activity :: hasWindowFocus () gibt den Booleschen Wert zurück, den Sie benötigen.

public class ActivityForegroundChecker extends TimerTask
{
    private static final long FOREGROUND_CHECK_PERIOD = 5000;
    private static final long FIRST_DELAY             = 3000;

    private Activity m_activity;
    private Timer    m_timer;

    public ActivityForegroundChecker (Activity p_activity)
    {
        m_activity = p_activity;
    }

    @Override
    public void run()
    {
        if (m_activity.hasWindowFocus() == true) {
            // Activity is on foreground
            return;
        }
        // Activity is on background.
    }

    public void start ()
    {
        if (m_timer != null) {
            return;
        }
        m_timer = new Timer();
        m_timer.schedule(this, FIRST_DELAY, FOREGROUND_CHECK_PERIOD);
    }

    public void stop ()
    {
        if (m_timer == null) {
            return;
        }
        m_timer.cancel();
        m_timer.purge();
        m_timer = null;
    }
}

Hier eine Beispielklasse, um die Sichtbarkeit Ihrer Aktivitäten von überall aus zu überprüfen. 

Denken Sie daran, dass, wenn Sie ein Dialogfeld anzeigen, das Ergebnis falsch ist, da der Dialog den Hauptfokus hat. Abgesehen davon ist es wirklich praktisch und zuverlässiger als die vorgeschlagenen Lösungen.

7
Burak Day

Zwei mögliche Lösungen:

1) Aktivitäts-Lifecycle-Rückrufe

Verwenden Sie eine Anwendung , die ActivityLifecycleCallbacks implementiert, und verwenden Sie diese, um Aktivitätenlebenszyklusereignisse in Ihrer Anwendung zu verfolgen. Beachten Sie, dass ActivityLifecycleCallbacks für Android api> = 14 sind. Für frühere Android-APIs müssen Sie sie selbst in alle Ihre Aktivitäten implementieren ;-)

Verwenden Sie Application , wenn Sie Zustände für Aktivitäten gemeinsam nutzen/speichern müssen.

2) Überprüfen Sie, ob Prozessinformationen ablaufen

Sie können den Status eines laufenden Prozesses mit dieser Klasse überprüfen RunningAppProcessInfo

Rufen Sie die Liste der laufenden Prozesse mit ActivityManager.getRunningAppProcesses () Ab und filtern Sie die Ergebnisliste, um nach der gewünschten RunningAppProcessInfo zu suchen und ihre "Wichtigkeit" zu überprüfen.

7
avianey

Ich habe ein Projekt auf github app-foreground-background-listen erstellt.

die verwendet eine sehr einfache Logik und funktioniert gut mit allen Android-API-Levels.

4
kiran boghra

Verwenden Sie die Zeitspanne zwischen Pause und Wiederaufnahme aus dem Hintergrund, um zu bestimmen, ob der Hintergrund aktiv ist

In der benutzerdefinierten Anwendung

private static boolean isInBackground;
private static boolean isAwakeFromBackground;
private static final int backgroundAllowance = 10000;

public static void activityPaused() {
    isInBackground = true;
    final Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            if (isInBackground) {
                isAwakeFromBackground = true;
            }
        }
    }, backgroundAllowance);
    Log.v("activity status", "activityPaused");
}

public static void activityResumed() {
    isInBackground = false;
    if(isAwakeFromBackground){
        // do something when awake from background
        Log.v("activity status", "isAwakeFromBackground");
    }
    isAwakeFromBackground = false;
    Log.v("activity status", "activityResumed");
}

In BaseActivity-Klasse

@Override
protected void onResume() {
  super.onResume();
  MyApplication.activityResumed();
}

@Override
protected void onPause() {
  super.onPause();
  MyApplication.activityPaused();
}
3
Kit

Ich denke, ich habe eine bessere Lösung. Weil Sie einfach MyApplication.activityResumed () einbauen können; zu jeder Aktivität um einen Punkt. 

Zuerst müssen Sie erstellen (wie CyberneticTwerkGuruOrc)

public class MyApplication extends Application {

  public static boolean isActivityVisible() {
    return activityVisible;
  }  

  public static void activityResumed() {
    activityVisible = true;
  }

  public static void activityPaused() {
    activityVisible = false;
  }

  private static boolean activityVisible;
}

Als Nächstes müssen Sie der AndroidManifest.xml-Klasse Application-Klasse hinzufügen

<application
    Android:name="your.app.package.MyApplication"
    Android:icon="@drawable/icon"
    Android:label="@string/app_name" >

Dann erstellen Sie die Klasse ActivityBase

public class ActivityBase extends Activity {

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

    @Override
    protected void onResume() {
        super.onResume();
        MyApplication.activityResumed();
    }
}

Wenn Sie eine neue Aktivität erstellen, können Sie sie ganz einfach um ActivityBase anstelle von Activity erweitern. 

public class Main extends ActivityBase {
    @Override
    protected void onResume() {
        super.onResume();
    }

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

Für mich ist es eine bessere Methode, weil man sich nur daran erinnern muss, von ActivityBase zu erweitern. Darüber hinaus können Sie Ihre Basisfunktion zukünftig erweitern. In meinem Fall habe ich Empfänger für meinen Dienst und Warnungen über das Netzwerk in einer Klasse hinzugefügt. 

Wenn Sie die Sichtbarkeit Ihrer App überprüfen möchten, können Sie einfach anrufen

MyApplication.isActivityVisible()
2
EliaszKubala

2019 können Sie mit Hilfe der neuen Support-Bibliothek 28+ oder AndroidX einfach Folgendes verwenden:

val isActivityInForeground = activity.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)

Sie können mehr in der Dokumentation nachlesen, um zu verstehen, was unter der Haube passiert ist. 

2
Alex Misulya

Dies kann auf effiziente Weise durch Verwendung von Application.ActivityLifecycleCallbacks erreicht werden.

Nehmen wir zum Beispiel den Namen der Aktivitätsklasse als ProfileActivity an, um herauszufinden, ob der Vordergrund oder Hintergrund im Vordergrund ist

zuerst müssen wir unsere Anwendungsklasse erstellen, indem wir Application Class erweitern.

was implementiert 

Application.ActivityLifecycleCallbacks

Lass meine Bewerbungsklasse wie folgt sein

Anwendungsklasse

public class AppController extends Application implements Application.ActivityLifecycleCallbacks {


private boolean activityInForeground;

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

//register ActivityLifecycleCallbacks  

    registerActivityLifecycleCallbacks(this);

}



public static boolean isActivityVisible() {
    return activityVisible;
}

public static void activityResumed() {
    activityVisible = true;
}

public static void activityPaused() {
    activityVisible = false;
}

private static boolean activityVisible;

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

}

@Override
public void onActivityStarted(Activity activity) {

}

@Override
public void onActivityResumed(Activity activity) {
    //Here you can add all Activity class you need to check whether its on screen or not

    activityInForeground = activity instanceof ProfileActivity;
}

@Override
public void onActivityPaused(Activity activity) {

}

@Override
public void onActivityStopped(Activity activity) {

}

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

}

@Override
public void onActivityDestroyed(Activity activity) {

}

public boolean isActivityInForeground() {
    return activityInForeground;
}
}

in der obigen Klasse gibt es eine Überschreibmethode onActivityResumed von ActivityLifecycleCallbacks.

 @Override
public void onActivityResumed(Activity activity) {
    //Here you can add all Activity class you need to check whether its on screen or not

    activityInForeground = activity instanceof ProfileActivity;
}

wenn sich alle Aktivitätsinstanzen befinden, die aktuell auf dem Bildschirm angezeigt werden, überprüfen Sie einfach, ob Ihre Aktivität auf dem Bildschirm angezeigt wird oder nicht.

Registrieren Sie Ihre Application-Klasse in manifest.xml

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

Um zu überprüfen, ob die Aktivität Vordergrund oder Hintergrund gemäß der obigen Lösung ist, rufen Sie die folgende Methode an den Orten auf, die Sie überprüfen möchten

AppController applicationControl = (AppController) getApplicationContext();
    if(applicationControl.isActivityInForeground()){
     Log.d("TAG","Activity is in foreground")
    }
    else
    {
      Log.d("TAG","Activity is in background")
    }
1
Ramz

Ich muss sagen, dass Ihr Workflow nicht auf Android-Standard basiert. In Android müssen Sie Ihre Aktivität nicht finish() ausführen, wenn Sie eine andere Aktivität in Intent öffnen möchten. Android bietet dem Benutzer die Möglichkeit, die Zurück-Taste zu verwenden, um von der Aktivität zurückzukehren, die Sie für Ihre App geöffnet haben.

Lassen Sie das System also einfach Ihre Aktivität stoppen und speichern Sie alles, wenn Ihre Aktivität zurückgerufen wird.

1
Owen Zhao

haben Sie versucht, Finish nicht aufzurufen und "Android: noHistory =" true "in das Manifest zu setzen?.

1
shaish

Wenn Sie wissen möchten, ob eine Aktivität Ihrer App auf dem Bildschirm sichtbar ist, können Sie Folgendes tun:

public class MyAppActivityCallbacks implements Application.ActivityLifecycleCallbacks {
private Set<Class<Activity>> visibleActivities = new HashSet<>();

@Override
public void onActivityResumed(Activity activity) {
    visibleActivities.add((Class<Activity>) activity.getClass());
}

@Override
public void onActivityStopped(Activity activity) {
     visibleActivities.remove(activity.getClass());
}

public boolean isAnyActivityVisible() {
    return !visibleActivities.isEmpty();
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}

@Override
public void onActivityStarted(Activity activity) {}

@Override
public void onActivityPaused(Activity activity) {}

@Override
public void onActivityDestroyed(Activity activity) {}

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

Erstellen Sie einfach ein Singleton dieser Klasse und setzen Sie es wie folgt in Ihre Anwendungsinstanz:

class App extends Application{
     @Override
     public void onCreate() {
         registerActivityLifecycleCallbacks(myAppActivityCallbacks);
     }
}

Dann können Sie die isAnyActivityVisible () - Methode Ihrer MyAppActivityCallbacks-Instanz überall verwenden!

1

Speichern Sie eine Flagge, wenn Sie pausiert oder fortgesetzt werden. Wenn Sie fortfahren, bedeutet dies, dass Sie sich im Vordergrund befinden

boolean  isResumed = false;

@Override
public void onPause() {
  super.onPause();    
  isResumed = false;
}

@Override
public void onResume() {
  super.onResume();    
  isResumed = true;
}

private void finishIfForeground() {
  if (isResumed) {
    finish();
  }
}
0
yoah

Wenn Sie finish() nur verwenden, um zu verhindern, dass neue Apps im Stapel (Task) Ihrer App gestartet werden, können Sie beim Start einer neuen Anwendung Intent.FLAG_ACTIVITY_NEW_TASK flag verwenden und finish() nicht aufrufen. Gemäß der Dokumentation ist dies das Flag, das zum Implementieren eines "Launcher" -Stilverhaltens verwendet wird.

// just add this line before you start an activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
0

Wäre Activity.onWindowFocusChanged(boolean hasFocus) hier nützlich? Das plus ein Flag auf Klassenebene, etwas wie isFocused, das von onWindowFocusChanged gesetzt wird, wäre eine einfache Möglichkeit, an jedem Punkt Ihrer Aktivität zu erkennen, ob sie fokussiert ist oder nicht. Beim Lesen der Dokumente sieht es so aus, als würde sie richtig "falsch" setzen, wenn sich die Aktivität nicht direkt im physischen "Vordergrund" befindet, beispielsweise wenn ein Dialogfeld angezeigt wird oder die Benachrichtigungsleiste heruntergezogen wird.

Beispiel:

boolean isFocused;
@Override
void onWindowFocusChanged (boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    isFocused = hasFocus;
}

void someMethod() {
    if (isFocused) {
        // The activity is the foremost object on the screen
    } else {
        // The activity is obscured or otherwise not visible
    }
}
0
InsanityOnABun

Warum nicht Broadcasts dafür verwenden? Die zweite Aktivität (die aktiv sein muss) kann eine lokale Sendung wie folgt senden:

//put this in onCreate(..) or any other lifecycle method that suits you best
//notice the string sent to the intent, it will be used to register a receiver!
Intent result = new Intent("broadcast identifier");
result.putString("some message");//this is optional
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(result);

dann schreibe einen einfachen Empfänger in die Splash-Aktivität:

//this goes on the class level (like a class/instance variable, not in a method) of your splash activity:
private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        //kill activity here!!!
        //mission accomplished!
    }
};

und registrieren Sie Ihren neuen Empfänger beim LocalBroadcastManager, um die Sendung Ihrer zweiten Aktivität zu hören:

//notice the string sent to the intent filter, this is where you tell the BroadcastManager which broadcasts you want to listen to!
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(receiver, new IntentFilter("broadcast identifier"));

Beachten Sie, dass Sie eine Konstante oder eine Zeichenfolgenressource für die Zeichenfolge "Broadcast-ID" verwenden können.

0
user1545072

Verwenden Sie diese Methoden in einem Activity.

isDestroyed()

Hinzugefügt in Api 17
Gibt true zurück, wenn der letzte Aufruf von onDestroy () für die Aktivität ausgeführt wurde. Diese Instanz ist also jetzt tot.

isFinishing()

Hinzugefügt in Api 1
Überprüfen Sie, ob diese Aktivität gerade beendet wird, entweder weil Sie finish () aufgerufen haben oder weil eine andere Person angefordert hat, dass sie beendet wird. Dies wird häufig in onPause () verwendet, um zu bestimmen, ob die Aktivität einfach angehalten oder vollständig beendet wird.


From Memory Leaks Documentation

Ein häufiger Fehler bei AsyncTask besteht darin, einen starken Verweis auf den Host Activity (oder Fragment) zu erfassen:

class MyActivity extends Activity {
  private AsyncTask<Void, Void, Void> myTask = new AsyncTask<Void, Void, Void>() {
    // Don't do this! Inner classes implicitly keep a pointer to their
    // parent, which in this case is the Activity!
  }
}

Dies ist ein Problem, da AsyncTask das übergeordnete Activity leicht überleben kann, z. B. wenn eine Konfigurationsänderung erfolgt, während die Task ausgeführt wird.

Der richtige Weg, dies zu tun, besteht darin, Ihre Aufgabe zu einer static -Klasse zu machen, die das übergeordnete Element nicht erfasst, und ein schwacher Verweis auf den Host Activity zu halten:

class MyActivity extends Activity {
  static class MyTask extends AsyncTask<Void, Void, Void> {
    // Weak references will still allow the Activity to be garbage-collected
    private final WeakReference<MyActivity> weakActivity;

    MyTask(MyActivity myActivity) {
      this.weakActivity = new WeakReference<>(myActivity);
    }

    @Override
    public Void doInBackground(Void... params) {
      // do async stuff here
    }

    @Override
    public void onPostExecute(Void result) {
      // Re-acquire a strong reference to the activity, and verify
      // that it still exists and is active.
      MyActivity activity = weakActivity.get();
      if (activity == null
          || activity.isFinishing()
          || activity.isDestroyed()) {
        // activity is no longer valid, don't do anything!
        return;
      }

      // The activity is still valid, do main-thread stuff here
    }
  }
}
0
Sanket Berde

Eine mögliche Lösung wäre das Setzen eines Flags, während der Systemdialog angezeigt wird. Überprüfen Sie dann in der onStop-Methode des Aktivitätslebenszyklus, ob das Flag vorhanden ist, und beenden Sie die Aktivität.

Wenn zum Beispiel der Systemdialog durch einen Knopfklick ausgelöst wird, könnte der Onclick-Listener ähnlich sein

private OnClickListener btnClickListener = new OnClickListener() {

    @Override
    public void onClick(View v) {           
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.setType("text/plain");
        CheckActivity.this.startActivity(Intent.createChooser(intent, "Complete action using"));
        checkFlag = true;  //flag used to check

    }
};

und auf einer Aktivität:

@Override
protected void onStop() {
    if(checkFlag){
        finish();
    }
    super.onStop();
}
0
Diya

Hier ist eine Lösung, die die Application-Klasse verwendet.

public class AppSingleton extends Application implements Application.ActivityLifecycleCallbacks {

private WeakReference<Context> foregroundActivity;


@Override
public void onActivityResumed(Activity activity) {
    foregroundActivity=new WeakReference<Context>(activity);
}

@Override
public void onActivityPaused(Activity activity) {
    String class_name_activity=activity.getClass().getCanonicalName();
    if (foregroundActivity != null && 
            foregroundActivity.get().getClass().getCanonicalName().equals(class_name_activity)) {
        foregroundActivity = null;
    }
}

//............................

public boolean isOnForeground(@NonNull Context activity_cntxt) {
    return isOnForeground(activity_cntxt.getClass().getCanonicalName());
}

public boolean isOnForeground(@NonNull String activity_canonical_name) {
    if (foregroundActivity != null && foregroundActivity.get() != null) {
        return foregroundActivity.get().getClass().getCanonicalName().equals(activity_canonical_name);
    }
    return false;
}
}

Sie können es einfach wie folgt verwenden,

((AppSingleton)context.getApplicationContext()).isOnForeground(context_activity);

Wenn Sie einen Verweis auf die erforderliche Aktivität haben oder den kanonischen Namen der Aktivität verwenden, können Sie herausfinden, ob sie im Vordergrund steht oder nicht. Diese Lösung ist möglicherweise nicht narrensicher. Daher sind Ihre Kommentare sehr willkommen.

0
TMtech

Ich weiß nicht, warum niemand über SharedPreferences sprach, für Aktivität A eine solche SharedPreference (zB in onPause ()):

SharedPreferences pref = context.getSharedPreferences(SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("is_activity_paused_a", true);
editor.commit();

Ich denke, das ist der verlässliche Weg, Aktivitäten sichtbar zu machen.

0
HZDI