Come rilevare quando un'app Android passa in secondo piano e torna in primo piano


382

Sto cercando di scrivere un'app che fa qualcosa di specifico quando viene riportata in primo piano dopo un certo periodo di tempo. Esiste un modo per rilevare quando un'app viene inviata in background o portata in primo piano?


2
Potrebbe essere quello di aggiungere un caso d'uso alla domanda perché non sembra ovvio, quindi non viene affrontato nelle risposte fornite. L'app può avviare un'altra app (ad esempio Galleria), che risiederà comunque nello stesso stack e apparirà come una delle schermate dell'app, quindi premere il pulsante Home. Nessuno dei metodi che si basano sul ciclo di vita dell'app (o persino sulla gestione della memoria) è in grado di rilevarlo. Attiverebbero lo stato di sfondo proprio quando appare Attività esterna, non quando si preme Home.
Dennis K,

Questa è la risposta che stai cercando: stackoverflow.com/a/42679191/2352699
Fred Porciúncula

Risposte:


98

I metodi onPause()e onResume()vengono chiamati quando l'applicazione viene portata in background e di nuovo in primo piano. Tuttavia, vengono anche chiamati quando l'applicazione viene avviata per la prima volta e prima che venga uccisa. Puoi leggere di più in Attività .

Non esiste alcun approccio diretto per ottenere lo stato dell'applicazione in background o in primo piano, ma anche io ho affrontato questo problema e ho trovato la soluzione con onWindowFocusChangede onStop.

Per maggiori dettagli, consulta qui Android: soluzione per rilevare quando un'app Android passa in background e torna in primo piano senza getRunningTasks o getRunningAppProcesses .


174
Tuttavia, questo approccio provoca falsi positivi come altri hanno sottolineato, perché questi metodi sono anche chiamati durante la transizione tra le attività nella stessa app.
John Lehmann,

9
È peggio di così. L'ho provato e talvolta l'onResume viene chiamato mentre il telefono è bloccato. Se vedi la definizione di onResume nella documentazione, troverai: Tieni presente che onResume non è il miglior indicatore che la tua attività è visibile all'utente; una finestra di sistema come il blocco tastiera potrebbe essere davanti. Usa onWindowFocusChanged (booleano) per sapere con certezza che la tua attività è visibile all'utente (ad esempio, per riprendere una partita). developer.android.com/reference/android/app/…
J-Rou

2
La soluzione pubblicata nel collegamento non utilizza onResume / onPause, invece una combinazione di onBackPressed, onStop, onStart e onWindowsFocusChanged. Ha funzionato per me e ho una gerarchia dell'interfaccia utente piuttosto complessa (con cassetti, visualizzatori dinamici, ecc.)
Martin Marconcini,

18
OnPause e onResume sono specifici dell'attività. Non applicazione. Quando un'app viene messa in background e quindi ripresa, riprende l'attività specifica in cui si trovava prima di passare allo sfondo. Ciò significa che sarà necessario implementare tutto ciò che si desidera fare riprendendo dallo sfondo in tutte le attività dell'applicazione. Credo che la domanda originale fosse cercare qualcosa come un "onResume" per l'applicazione e non per l'attività.
SysHex,

4
Non riesco a credere che un'API corretta non sia offerta per un'esigenza così comune. Inizialmente pensavo che UserLeaveHint () l'avrebbe tagliato, ma non puoi dire se l'utente sta lasciando l'applicazione o no
atsakiridis,

197

2018: Android supporta questo in modo nativo attraverso i componenti del ciclo di vita.

AGGIORNAMENTO di marzo 2018 : ora esiste una soluzione migliore. Vedi ProcessLifecycleOwner . Dovrai utilizzare i nuovi componenti dell'architettura 1.1.0 (la più recente in questo momento) ma è specificamente progettato per farlo.

C'è un semplice esempio fornito in questa risposta, ma ho scritto un'app di esempio e un post sul blog a riguardo.

Da quando l'ho scritto nel 2014, sono nate diverse soluzioni. Alcuni hanno funzionato, altri si pensava funzionassero , ma avevano difetti (incluso il mio!) E noi, come comunità (Android), abbiamo imparato a convivere con le conseguenze e abbiamo scritto soluzioni alternative per i casi speciali.

Non dare per scontato che un singolo frammento di codice sia la soluzione che stai cercando, è improbabile che sia così; meglio ancora, cerca di capire cosa fa e perché lo fa.

La MemoryBossclasse non è mai stata effettivamente utilizzata da me come scritto qui, era solo un pezzo di pseudo codice che funzionava.

A meno che non ci sia una ragione valida per non usare i nuovi componenti dell'architettura (e ce ne sono alcuni, specialmente se si prendono di mira le vecchie vecchie API), quindi andare avanti e usarli. Sono tutt'altro che perfetti, ma nessuno dei due lo era ComponentCallbacks2.

AGGIORNAMENTO / NOTE (novembre 2015) : le persone hanno fatto due commenti, il primo è che >=dovrebbe essere usato invece che ==perché la documentazione afferma che non si dovrebbero verificare i valori esatti . Questo va bene per la maggior parte dei casi, ma tenere a mente che se solo si cura di fare qualcosa quando l'applicazione è andato al fondo, si dovrà utilizzare == e anche combinarlo con un'altra soluzione (come callback attività del ciclo di vita), oppure si potrebbe non ottenere l'effetto desiderato. L'esempio (e questo è successo a me) è che se vuoi bloccarela tua app con una schermata della password quando passa in background (come 1Password se la conosci), potresti accidentalmente bloccare l'app se hai poca memoria e stai improvvisamente testando >= TRIM_MEMORY, perché Android attiverà una LOW MEMORYchiamata e questo è più alto del tuo. Quindi fai attenzione a come / cosa testare.

Inoltre, alcune persone hanno chiesto come rilevare quando torni.

Il modo più semplice a cui riesco a pensare è spiegato di seguito, ma dato che alcune persone non lo conoscono, sto aggiungendo un pseudo codice proprio qui. Supponendo che tu abbia YourApplicatione le MemoryBossclassi, nel tuo class BaseActivity extends Activity(dovrai crearne una se non ne hai una).

@Override
protected void onStart() {
    super.onStart();

    if (mApplication.wasInBackground()) {
        // HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
        mApplication.setWasInBackground(false);
    }
}

Raccomando onStart perché le finestre di dialogo possono mettere in pausa un'attività, quindi scommetto che non vuoi che la tua app pensi "è andata in secondo piano" se tutto ciò che hai fatto è stato visualizzare una finestra di dialogo a schermo intero, ma il tuo chilometraggio può variare.

E questo è tutto. Il codice nel blocco if sarà eseguito solo una volta , anche se si va a un'altra attività, quello nuovo (che anche extends BaseActivity) riferirà wasInBackgroundè falsein modo da non eseguire il codice, fino a quando onMemoryTrimmedviene chiamato e il flag è impostato su true nuovo .

Spero che aiuti.

AGGIORNAMENTO / NOTE (aprile 2015) : Prima di andare su Copia e incolla su questo codice, nota che ho trovato un paio di casi in cui potrebbe non essere affidabile al 100% e devo combinarlo con altri metodi per ottenere i migliori risultati. In particolare, ci sono due casi noti in cui onTrimMemorynon è garantita l'esecuzione della richiamata:

  1. Se il telefono blocca lo schermo mentre l'app è visibile (diciamo che il dispositivo si blocca dopo nn minuti), questo callback non viene chiamato (o non sempre) perché la schermata di blocco è solo in cima, ma l'app è ancora "in esecuzione" anche se coperta.

  2. Se il tuo dispositivo ha una memoria relativamente bassa (e sotto stress di memoria), il sistema operativo sembra ignorare questa chiamata e passare direttamente a livelli più critici.

Ora, a seconda di quanto sia importante che tu sappia quando la tua app è passata in background, potresti aver bisogno o meno di estendere questa soluzione insieme per tenere traccia del ciclo di vita delle attività e quant'altro.

Tieni a mente quanto sopra e fai in modo di avere un buon team di controllo qualità;)

FINE AGGIORNAMENTO

Potrebbe essere tardi, ma esiste un metodo affidabile in Ice Cream Sandwich (API 14) e versioni successive .

Si scopre che quando l'app non ha più un'interfaccia utente visibile, viene attivato un callback. Il callback, che è possibile implementare in una classe personalizzata, si chiama ComponentCallbacks2 (sì, con un due). Questo callback è disponibile solo in API Level 14 (Ice Cream Sandwich) e versioni successive.

Praticamente ricevi una chiamata al metodo:

public abstract void onTrimMemory (int level)

Il livello è 20 o più specificamente

public static final int TRIM_MEMORY_UI_HIDDEN

Ho provato questo e funziona sempre, perché il livello 20 è solo un "suggerimento" che potresti voler rilasciare alcune risorse poiché la tua app non è più visibile.

Per citare i documenti ufficiali:

Livello per onTrimMemory (int): il processo mostrava un'interfaccia utente e non lo fa più. A questo punto è necessario rilasciare grandi allocazioni con l'interfaccia utente per consentire una migliore gestione della memoria.

Ovviamente, dovresti implementarlo per fare effettivamente ciò che dice (elimina la memoria che non è stata utilizzata in un determinato momento, cancella alcune raccolte che sono rimaste inutilizzate, ecc. Le possibilità sono infinite (vedi i documenti ufficiali per altri possibili altro livelli critici ).

Ma la cosa interessante è che il sistema operativo ti sta dicendo: HEY, la tua app è passata in secondo piano!

Che è esattamente quello che volevi sapere in primo luogo.

Come determini quando sei tornato?

Bene, è facile, sono sicuro che hai un "BaseActivity" in modo da poter usare onResume () per segnalare il fatto che sei tornato. Perché l'unica volta che dirai che non sei tornato è quando ricevi effettivamente una chiamata al onTrimMemorymetodo sopra .

Funziona. Non ottieni falsi positivi. Se un'attività riprende, sei tornato, il 100% delle volte. Se l'utente torna di nuovo sul retro, si riceve un'altra onTrimMemory()chiamata.

Devi iscrivere le tue attività (o meglio ancora, un corso personalizzato).

Il modo più semplice per garantirti di ricevere sempre questo è creare una classe semplice come questa:

public class MemoryBoss implements ComponentCallbacks2 {
    @Override
    public void onConfigurationChanged(final Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // We're in the Background
        }
        // you might as well implement some memory cleanup here and be a nice Android dev.
    }
}

Per usarlo, nell'implementazione dell'applicazione ( ne hai uno, DESTRA? ), Fai qualcosa del tipo:

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

Se si crea un, Interfaceè possibile aggiungere un elsea quello ife implementare ComponentCallbacks(senza il 2) utilizzato in qualsiasi cosa sotto API 14. Quel callback ha solo il onLowMemory()metodo e non viene chiamato quando si passa in background , ma è necessario utilizzarlo per tagliare la memoria .

Ora avvia l'app e premi home. Il tuo onTrimMemory(final int level)metodo dovrebbe essere chiamato (suggerimento: aggiungi registrazione).

L'ultimo passaggio consiste nell'annullare la registrazione dal callback. Probabilmente il posto migliore è il onTerminate()metodo della tua app, ma quel metodo non viene chiamato su un dispositivo reale:

/**
 * This method is for use in emulated process environments.  It will
 * never be called on a production Android device, where processes are
 * removed by simply killing them; no user code (including this callback)
 * is executed when doing so.
 */

Quindi, a meno che tu non abbia davvero una situazione in cui non desideri più essere registrato, puoi tranquillamente ignorarlo, poiché il tuo processo sta comunque morendo a livello di sistema operativo.

Se decidi di annullare la registrazione a un certo punto (se, ad esempio, fornisci un meccanismo di arresto per la tua app per ripulire e morire), puoi fare:

unregisterComponentCallbacks(mMemoryBoss);

E questo è tutto.


Quando si controlla questo da un servizio, sembra attivarsi solo quando si preme il pulsante Home. Premendo il pulsante Indietro non si attiva questo su KitKat.
Impara OpenGL ES il

Il servizio non ha UI quindi potrebbe essere correlato a quello. Effettua il check-in dell'attività di base, non su un servizio. Vuoi sapere quando la tua interfaccia utente è nascosta (e forse dire al servizio, quindi va in primo piano)
Martin Marconcini,

1
Non funziona quando si spegne il telefono. Non è attivato.
Juangcg,

2
L'uso di ComponentCallbacks2.onTrimMemory () (in combinazione con ActivityLifecycleCallbacks) è l'unica soluzione affidabile che ho trovato finora, grazie Martin! Per chi fosse interessato, vedi la mia risposta fornita.
rickul,

3
Uso questo metodo da un anno ed è sempre stato affidabile per me. È bello sapere che anche altre persone lo usano. Uso solo ciò level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENche evita il problema nel tuo aggiornamento, punto 2. Per quanto riguarda il punto 1, non è un problema per me, poiché l'app non è andata davvero in secondo piano, quindi è così che dovrebbe funzionare.
sorianiv,

175

Ecco come sono riuscito a risolverlo. Funziona sul presupposto che l'utilizzo di un riferimento temporale tra le transizioni di attività fornirà molto probabilmente prove adeguate che un'app è stata "in background" o meno.

Innanzitutto, ho usato un'istanza android.app.Application (chiamiamola MyApplication) che ha un Timer, un TimerTask, una costante per rappresentare il numero massimo di millisecondi che la transizione da un'attività all'altra potrebbe ragionevolmente richiedere (sono andato con un valore di 2s) e un valore booleano per indicare se l'app era "in background" o meno:

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

L'applicazione fornisce anche due metodi per l'avvio e l'arresto del timer / attività:

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

L'ultimo pezzo di questa soluzione è aggiungere una chiamata a ciascuno di questi metodi dagli eventi onResume () e onPause () di tutte le attività o, preferibilmente, in un'attività di base da cui ereditano tutte le attività concrete:

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

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

Quindi, nel caso in cui l'utente stia semplicemente navigando tra le attività della tua app, onPause () dell'attività in partenza avvia il timer, ma quasi immediatamente la nuova attività immessa annulla il timer prima che possa raggiungere il tempo di transizione massimo. E così anche InBackground sarebbe falso .

D'altra parte, quando un'attività viene in primo piano dal Launcher, il dispositivo si sveglia, termina la chiamata, ecc., Molto probabilmente l'attività del timer eseguita prima di questo evento, e quindi wasInBackground era impostata su true .


4
Ciao d60402, la tua risposta è davvero utile .. grazie mille per questa risposta ... piccolo avviso .. MyApplication dovrebbe menzionare nel tag Manifest file application come android: name = "MyApplication", altrimenti si blocca l'app ... solo per aiutare qualcuno come me
praveenb

2
segno del grande programmatore, soluzione semplice a uno dei problemi più complicati che mi sia mai imbattuto.
Aashish Bhatnagar,

2
Ottima soluzione! Grazie. Se qualcuno riceve l'errore "ClassCastException", potresti aver perso l'aggiunta nel tag dell'applicazione all'interno del tuo Manifest.xml <applicazione android: name = "your.package.MyApplication"
Wahib Ul Haq,

27
Questa è un'implementazione piacevole e semplice. Tuttavia, credo che questo dovrebbe essere implementato in onStart / onStop piuttosto che onPause / onResume. L'onPause verrà chiamato anche se avvio una finestra di dialogo che copre parzialmente l'attività. E chiudere la finestra di dialogo chiamerebbe effettivamente Resume per far sembrare che l'app sia appena arrivata in primo piano
Shubhayu

7
Spero di usare una variante di questa soluzione. Il punto sui dialoghi identificati sopra è un problema per me, quindi ho provato il suggerimento di @ Shubhayu (onStart / onStop). Ciò non aiuta tuttavia perché quando si va A-> B, onStart () dell'attività B viene chiamato prima di onStop () dell'attività.
Trevor,

150

Modifica: i nuovi componenti dell'architettura hanno portato qualcosa di promettente: ProcessLifecycleOwner , vedi la risposta di @ vokilam


La soluzione effettiva secondo un discorso I / O di Google :

class YourApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(AppLifecycleTracker())
  }

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

  override fun onActivityStarted(activity: Activity?) {
    if (numStarted == 0) {
      // app went to foreground
    }
    numStarted++
  }

  override fun onActivityStopped(activity: Activity?) {
    numStarted--
    if (numStarted == 0) {
      // app went to background
    }
  }

}

Sì. So che è difficile credere che questa semplice soluzione funzioni dal momento che abbiamo così tante soluzioni strane qui.

Ma c'è speranza.


3
Funziona perfettamente! Ho già provato così tante soluzioni strane che avevano così tanti difetti ... grazie mille! Lo cerco da un po '.
Eggakin Baconwalker,

7
Funziona per più attività, ma per uno - onrotate indicherà che tutte le attività sono sparite o in background
pesce morto

2
@Shyri hai ragione, ma fa parte di questa soluzione, quindi devi preoccuparti. Se Firebase si basa su questo, penso che anche la mia app mediocre possa farlo :) Ottima risposta BTW.
ElliotM

3
@deadfish Controlla il collegamento a I / O fornito nella parte superiore della risposta. È possibile controllare gli intervalli di tempo tra l'interruzione dell'attività e iniziare a determinare se si è davvero passati allo sfondo o meno. Questa è una soluzione geniale, in realtà.
Alex Berdnikov,

2
Esiste una soluzione Java? Questo è kotlin.
Giacomo Bartoli,

116

ProcessLifecycleOwner sembra essere anche una soluzione promettente.

ProcessLifecycleOwner invierà ON_START, ON_RESUMEeventi, come una prima attività si muove attraverso questi eventi. ON_PAUSE, gli ON_STOPeventi verranno inviati con un ritardo dopo che un'ultima attività li ha attraversati. Questo ritardo è abbastanza lungo da garantire che ProcessLifecycleOwnernon invierà alcun evento se le attività vengono distrutte e ricreate a causa di una modifica della configurazione.

Un'implementazione può essere semplice come

class AppLifecycleListener : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onMoveToForeground() { // app moved to foreground
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onMoveToBackground() { // app moved to background
    }
}

// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())

Secondo il codice sorgente, il valore di ritardo corrente è 700ms.

Anche l'utilizzo di questa funzione richiede dependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

10
Si noti che è necessario aggiungere le dipendenze del ciclo di vita implementation "android.arch.lifecycle:extensions:1.0.0"e annotationProcessor "android.arch.lifecycle:compiler:1.0.0"dal repository di Google (es. google())
Sir Codesalot,

1
Per me ha funzionato alla grande, grazie. Ho dovuto usare api 'android.arch.lifecycle: extensions: 1.1.0' invece di implementazione a causa di un errore che indica che la dipendenza Android ha una versione diversa per il percorso di classe di compilazione e runtime.
FSUWX2011

Questa è un'ottima soluzione perché funziona in moduli senza bisogno di un riferimento di attività!
Max

Questo non funziona in caso di arresto anomalo dell'app. Esiste una soluzione per ottenere l'evento crash dell'app anche attraverso questa soluzione
tejraj

Ottima soluzione Mi ha salvato la giornata.
Sunny

69

Sulla base della risposta di Martín Marconcinis (grazie!) Ho finalmente trovato una soluzione affidabile (e molto semplice).

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
    private static boolean isInBackground = false;

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

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){
            Log.d(TAG, "app went to foreground");
            isInBackground = false;
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(int i) {
        if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
            Log.d(TAG, "app went to background");
            isInBackground = true;
        }
    }
}

Quindi aggiungilo a onCreate () della tua classe Application

public class MyApp extends android.app.Application {

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

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}

Puoi mostrare come lo usi in un'app, posso chiamarlo dalla classe App o da qualche altra parte?
JPM,

questo è perfetto grazie !! funziona benissimo nei test finora
aherrick il

Questo esempio se incompleto. Che cos'è registerActivityLifecycleCallbacks?
Noman,

è un metodo nella classe android.app.Application
rickul

1
ben fatto +1 per andare in cima, perché è perfetto, non guardare altre risposte, questo si basa sulla risposta di @reno ma con un vero esempio
Stoycho Andreev

63

Usiamo questo metodo. Sembra troppo semplice per funzionare, ma è stato ben testato nella nostra app e in effetti funziona sorprendentemente bene in tutti i casi, incluso andare alla schermata principale con il pulsante "home", con il pulsante "return" o dopo il blocco dello schermo. Provaci.

L'idea è, quando in primo piano, Android inizia sempre una nuova attività poco prima di interrompere quella precedente. Non è garantito, ma funziona così. A proposito, Flurry sembra usare la stessa logica (solo una supposizione, non l'ho verificato, ma si aggancia agli stessi eventi).

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();       
        sessionDepth++;
        if(sessionDepth == 1){
        //app came to foreground;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

Modifica: come da commenti, ci siamo anche spostati su onStart () nelle versioni successive del codice. Inoltre, sto aggiungendo super chiamate, che mancavano dal mio post iniziale, perché questo era più un concetto che un codice funzionante.


2
Questa è la risposta più affidabile, anche se utilizzo onStart anziché onResume.
Greg Ennis,

Dovresti aggiungere chiamate a super.onResume () e super.onStop () nei metodi overriden. Altrimenti viene lanciato un android.app.SuperNotCalledException.
Jan Laussmann,

1
per me non funziona ... o almeno attiva l'evento quando si ruota anche il dispositivo (che è una specie di imho falso positivo).
Noya,

Soluzione molto semplice ed efficace! Ma non sono sicuro che funzioni con attività parzialmente trasparenti che rendono visibili alcune parti dell'attività precedente. Dalla documentazione, onStop is called when the activity is no longer visible to the user.
Nicolas Buquet,

3
cosa succede se l'utente cambia l'orientamento sulla prima attività? Riferirà che l'app è passata in background, il che non è vero. Come gestite questo scenario?
Nimrod Dayan,

54

Se la tua app è composta da più attività e / o attività in pila come un widget della barra delle schede, la sostituzione di onPause () e onResume () non funzionerà. Vale a dire quando si avvia una nuova attività le attività correnti verranno messe in pausa prima che la nuova venga creata. Lo stesso vale quando si termina (usando il pulsante "indietro") un'attività.

Ho trovato due metodi che sembrano funzionare come desiderato.

Il primo richiede l'autorizzazione GET_TASKS ed è costituito da un metodo semplice che controlla se l'attività principale in esecuzione sul dispositivo appartiene all'applicazione, confrontando i nomi dei pacchetti:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

Questo metodo è stato trovato nel framework Droid-Fu (ora chiamato Ignition).

Il secondo metodo che ho implementato da solo non richiede l'autorizzazione GET_TASKS, il che è positivo. Invece è un po 'più complicato da implementare.

Nella tua classe MainApplication hai una variabile che tiene traccia del numero di attività in esecuzione nella tua applicazione. In onResume () per ogni attività aumenti la variabile e in onPause () la diminuisci.

Quando il numero di attività in esecuzione raggiunge 0, l'applicazione viene messa in background SE sono vere le seguenti condizioni:

  • L'attività in pausa non è terminata (è stato utilizzato il pulsante "indietro"). Questo può essere fatto usando il metodo activity.isFinishing ()
  • Non è stata avviata una nuova attività (stesso nome del pacchetto). È possibile sovrascrivere il metodo startActivity () per impostare una variabile che lo indica e quindi ripristinarlo in onPostResume (), che è l'ultimo metodo da eseguire quando viene creata / ripresa l'attività.

Quando è possibile rilevare che l'applicazione si è dimessa in background, è facile rilevare anche quando viene riportata in primo piano.


18
Google probabilmente rifiuterà un'app che utilizza ActivityManager.getRunningTasks (). La documentazione afferma che è solo a scopo di sviluppo. developer.android.com/reference/android/app/…
Sky Kelsey,


1
Ho scoperto che dovevo usare una combinazione di questi approcci. onUserLeaveHint () è stato chiamato all'avvio di un'attività in 14. `@Override public void onUserLeaveHint () {inBackground = isApplicationBroughtToBackground (); } `
elenco della barca

7
Gli utenti non saranno troppo contenti di usare una potente autorizzazione android.permission.GET_TASKS.
MSquare,

6
getRunningTasks è deprecato nel livello API 21.
Noya,

33

Crea una classe che si estende Application. Poi in esso possiamo usare il suo metodo di sostituzione, onTrimMemory().

Per rilevare se l'applicazione è passata in background, utilizzeremo:

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }

1
Anche per FragmentActivityte potresti voler aggiungere level == ComponentCallbacks2.TRIM_MEMORY_COMPLETEanche.
Srujan Simha

2
Grazie per aver indicato questo metodo, devo mostrare una finestra di dialogo Pin ogni volta che l'utente riprende l'attività per lo sfondo, ha usato questo metodo per scrivere un valore pref e lo ha verificato su baseActivity.
Sam,

18

Prendi in considerazione l'utilizzo di onUserLeaveHint. Questo verrà chiamato solo quando l'app passa in background. onPause avrà casi angolari da gestire, poiché può essere chiamato per altri motivi; ad esempio se l'utente apre un'altra attività nella tua app come la pagina delle impostazioni, il metodo onPause della tua attività principale verrà chiamato anche se sono ancora nella tua app; tenere traccia di ciò che sta accadendo porterà a bug quando invece puoi semplicemente usare il callback onUserLeaveHint che fa quello che stai chiedendo.

Quando si chiama UserLeaveHint, è possibile impostare un flag booleano inBackground su true. Quando viene chiamato onResume, supponi di essere tornato in primo piano solo se è impostato il flag inBackground. Questo perché onResume verrà chiamato anche sulla tua attività principale se l'utente si trovava nel menu delle impostazioni e non ha mai lasciato l'app.

Ricorda che se l'utente preme il pulsante Home mentre si trova nella schermata delle impostazioni, onUserLeaveHint verrà richiamato nell'attività delle impostazioni e quando ritorna acceso, verrà richiamato Ripristino nell'attività delle impostazioni. Se hai solo questo codice di rilevamento nella tua attività principale, perderai questo caso d'uso. Per avere questo codice in tutte le tue attività senza duplicare il codice, disponi di una classe di attività astratta che estende l'attività e inserisci il tuo codice comune. Quindi ogni attività che hai può estendere questa attività astratta.

Per esempio:

public abstract AbstractActivity extends Activity {
    private static boolean inBackground = false;

    @Override
    public void onResume() {
        if (inBackground) {
            // You just came from the background
            inBackground = false;
        }
        else {
            // You just returned from another activity within your own app
        }
    }

    @Override
    public void onUserLeaveHint() {
        inBackground = true;
    }
}

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}

19
onUserLeaveHint viene anche chiamato durante la navigazione verso un'altra attività
Jonas Stawski,

3
onUserLeaveHint non viene chiamato quando, ad esempio, arriva una telefonata e l'attività di chiamata diventa attiva, quindi questo ha anche un caso limite: potrebbero esserci anche altri casi, poiché è possibile aggiungere un flag all'intento per sopprimere la chiamata onUserLeaveHint. developer.android.com/reference/android/content/…
Groxx

1
Inoltre, onResume non funziona bene. L'ho provato e talvolta l'onResume viene chiamato mentre il telefono è bloccato. Se vedi la definizione di onResume nella documentazione, troverai: Tieni presente che onResume non è il miglior indicatore che la tua attività è visibile all'utente; una finestra di sistema come il blocco tastiera potrebbe essere in primo piano. Usa onWindowFocusChanged (booleano) per sapere con certezza che la tua attività è visibile all'utente (ad esempio, per riprendere una partita). developer.android.com/reference/android/app/…
J-Rou

questa soluzione non aiuta a decidere in primo piano / sfondo se ci sono più attività. Per favore riferisciti a stackoverflow.com/questions/3667022/…
Raj Trivedi

14

ActivityLifecycleCallbacks potrebbe essere interessante, ma non è ben documentato.

Tuttavia, se si chiama registerActivityLifecycleCallbacks (), si dovrebbe essere in grado di ottenere richiamate per quando le attività vengono create, distrutte, ecc. È possibile chiamare getComponentName () per l'attività.


11
Dal momento che api livello 14 = \
imort

Sembra che questo sia pulito e funzioni per me. Grazie
duanbo1983 il

In che modo questo differisce dalla risposta accettata, entrambi si affidano allo stesso ciclo di vita delle attività, giusto?
Saitama,

13

Il pacchetto android.arch.lifecycle fornisce classi e interfacce che ti consentono di creare componenti sensibili al ciclo di vita

L'applicazione deve implementare l'interfaccia LifecycleObserver:

public class MyApplication extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

Per fare ciò, devi aggiungere questa dipendenza al tuo file build.gradle:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

Come raccomandato da Google, è necessario ridurre al minimo il codice eseguito nei metodi di ciclo di vita delle attività:

Un modello comune è implementare le azioni dei componenti dipendenti nei metodi del ciclo di vita di attività e frammenti. Tuttavia, questo modello porta a una cattiva organizzazione del codice e alla proliferazione degli errori. Utilizzando componenti compatibili con il ciclo di vita, è possibile spostare il codice dei componenti dipendenti dai metodi del ciclo di vita e nei componenti stessi.

Puoi leggere di più qui: https://developer.android.com/topic/libraries/architecture/lifecycle


e aggiungilo per manifestare come: <application android: name = ". AnotherApp">
Dan Alboteanu

9

Nella tua applicazione aggiungi il callback e controlla l'attività di root in questo modo:

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

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

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}

Vorrei prendere in considerazione l'utilizzo di questo modo di implementazione. Una transizione da un'attività all'altra richiede solo pochi millisecondi. In base al momento in cui l'ultima attività scompare, si può considerare di riconnettere l'utente con una strategia specifica.
drindt,

6

Ho creato un progetto su Github app-foreground-background-hear

Crea una BaseActivity per tutte le attività nella tua applicazione.

public class BaseActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    public static boolean isAppInFg = false;
    public static boolean isScrInFg = false;
    public static boolean isChangeScrFg = false;

    @Override
    protected void onStart() {
        if (!isAppInFg) {
            isAppInFg = true;
            isChangeScrFg = false;
            onAppStart();
        }
        else {
            isChangeScrFg = true;
        }
        isScrInFg = true;

        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (!isScrInFg || !isChangeScrFg) {
            isAppInFg = false;
            onAppPause();
        }
        isScrInFg = false;
    }

    public void onAppStart() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();

        // Your code
    }

    public void onAppPause() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();

        // Your code
    }
}

Ora usa BaseActivity come una super classe di tutte le tue attività come MainActivity estende BaseActivity e onAppStart verrà chiamato quando avvii l'applicazione e onAppPause () verrà chiamato quando l'applicazione passa in background da qualsiasi schermata.


@kiran boghra: ci sono falsi positivi nella tua soluzione?
Harish Vishwakarma,

Risposta perfetta la funzione onStart () e onStop () può essere utilizzata in questo caso. che ti dice della tua app
Pir Fahim Shah

6

Questo è abbastanza facile con ProcessLifecycleOwner

Aggiungi queste dipendenze

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

A Kotlin :

class ForegroundBackgroundListener : LifecycleObserver {


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun startSomething() {
        Log.v("ProcessLog", "APP IS ON FOREGROUND")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopSomething() {
        Log.v("ProcessLog", "APP IS IN BACKGROUND")
    }
}

Quindi nella tua attività di base:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

Vedi il mio articolo su questo argomento: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48


5

È possibile utilizzare ProcessLifecycleOwner collegando ad esso un osservatore del ciclo di vita.

  public class ForegroundLifecycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onAppCreated() {
        Timber.d("onAppCreated() called");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppStarted() {
        Timber.d("onAppStarted() called");
    }

    @OnLifecycleEvent(Event.ON_RESUME)
    public void onAppResumed() {
        Timber.d("onAppResumed() called");
    }

    @OnLifecycleEvent(Event.ON_PAUSE)
    public void onAppPaused() {
        Timber.d("onAppPaused() called");
    }

    @OnLifecycleEvent(Event.ON_STOP)
    public void onAppStopped() {
        Timber.d("onAppStopped() called");
    }
}

quindi nella onCreate()classe della tua Applicazione chiami questo:

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

con questo sarai in grado di catturare gli eventi di ON_PAUSEe ON_STOPdella tua applicazione che si verificano quando va in background.


4

Non ci sono metodi di ciclo di vita semplici per dirti quando l'intera applicazione passa in primo piano.

L'ho fatto in modo semplice. Seguire le istruzioni seguenti per rilevare la fase di background / primo piano dell'applicazione.

Con un po 'di soluzione, è possibile. Qui ActivityLifecycleCallbacks viene in soccorso. Lasciami fare un passo alla volta.

  1. Innanzitutto, crea una classe che estende android.app.Application e implementa l' interfaccia ActivityLifecycleCallbacks . In Application.onCreate (), registrare il callback.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  2. Registrare la classe “App” nel Manifesto come sotto, <application android:name=".App".

  3. Ci sarà almeno un'attività nello stato di avvio quando l'app è in primo piano e non ci sarà attività nello stato di avvio quando l'app è in background.

    Dichiarare 2 variabili come di seguito nella classe "App".

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;

    activityReferences manterrà il conteggio del numero di attività nel stato avviato . isActivityChangingConfigurationsè un flag per indicare se l'attività corrente sta attraversando un cambiamento di configurazione come un interruttore di orientamento.

  4. Utilizzando il seguente codice è possibile rilevare se l'app viene visualizzata in primo piano.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  5. Ecco come rilevare se l'app passa in background.

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }

Come funziona:

Questo è un piccolo trucco fatto con il modo in cui i metodi del ciclo di vita sono chiamati in sequenza. Vorrei illustrare uno scenario.

Si supponga che l'utente avvii l'app e che venga avviata l'attività di avvio A. Le chiamate del ciclo di vita saranno,

A.onCreate ()

A.onStart () (++ activityReferences == 1) (L'app entra in primo piano)

A.onResume ()

Ora l'attività A avvia l'attività B.

A.onPause ()

B.onCreate ()

B.onStart () (++ activityReferences == 2)

B.onResume ()

A.onStop () (--activityReferences == 1)

Quindi l'utente torna indietro dall'attività B,

B.onPause ()

A.onStart () (++ activityReferences == 2)

A.onResume ()

B.onStop () (--activityReferences == 1)

B.onDestroy ()

Quindi l'utente preme il pulsante Home,

A.onPause ()

A.onStop () (--activityReferences == 0) (L'app entra in background)

Nel caso, se l'utente preme il pulsante Home dall'attività B invece del pulsante Indietro, sarà sempre lo stesso e attivitàReferenze saranno 0. Quindi, possiamo rilevare come l'app che entra in background.

Quindi, qual è il ruolo di isActivityChangingConfigurations? Nello scenario sopra, supponiamo che l'attività B cambi l'orientamento. La sequenza di richiamata sarà,

B.onPause ()

B.onStop () (--activityReferences == 0) (L'app entra in Background ??)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (L'app entra in primo piano ??)

B.onResume ()

Ecco perché abbiamo un ulteriore controllo isActivityChangingConfigurationsper evitare lo scenario quando l'attività sta attraversando le modifiche alla configurazione.


3

Ho trovato un buon metodo per rilevare l'applicazione sia in primo piano che in background. Ecco il mio codice . Spero che questo ti aiuti.

/**
 * Custom Application which can detect application state of whether it enter
 * background or enter foreground.
 *
 * @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
 */
 public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {

public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;

private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;

private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;

@Override
public void onCreate() {
    super.onCreate();
    mCurrentState = STATE_UNKNOWN;
    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    // mCurrentState = STATE_CREATED;
}

@Override
public void onActivityStarted(Activity activity) {
    if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
        if (mStateFlag == FLAG_STATE_BACKGROUND) {
            applicationWillEnterForeground();
            mStateFlag = FLAG_STATE_FOREGROUND;
        }
    }
    mCurrentState = STATE_STARTED;

}

@Override
public void onActivityResumed(Activity activity) {
    mCurrentState = STATE_RESUMED;

}

@Override
public void onActivityPaused(Activity activity) {
    mCurrentState = STATE_PAUSED;

}

@Override
public void onActivityStopped(Activity activity) {
    mCurrentState = STATE_STOPPED;

}

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

}

@Override
public void onActivityDestroyed(Activity activity) {
    mCurrentState = STATE_DESTROYED;
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidEnterBackground();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidDestroyed();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }
}

/**
 * The method be called when the application been destroyed. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidDestroyed();

/**
 * The method be called when the application enter background. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidEnterBackground();

/**
 * The method be called when the application enter foreground.
 */
protected abstract void applicationWillEnterForeground();

}


3

Puoi usare:

void protetto onRestart ()

Differire tra nuovi avviamenti e riavvii.

inserisci qui la descrizione dell'immagine


3

Modifica 2: Quello che ho scritto sotto non funzionerà davvero. Google ha rifiutato un'app che include una chiamata a ActivityManager.getRunningTasks (). Dalla documentazione , è evidente che questa API è solo a scopo di debug e sviluppo. Aggiornerò questo post non appena avrò tempo di aggiornare il progetto GitHub di seguito con un nuovo schema che utilizza i timer ed è quasi altrettanto buono.

Modifica 1: ho scritto un post sul blog e ho creato un semplice repository GitHub per renderlo davvero facile.

La risposta accettata e quella più votata non sono entrambe l'approccio migliore. L'implementazione della risposta più votata di isApplicationBroughtToBackground () non gestisce la situazione in cui l'attività principale dell'applicazione sta cedendo a un'attività definita nella stessa applicazione, ma ha un pacchetto Java diverso. Ho trovato un modo per farlo che funzionerà in quel caso.

Chiamalo in onPause () e ti dirà se l'applicazione sta andando in background perché è stata avviata un'altra applicazione o l'utente ha premuto il pulsante Home.

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

Cordiali saluti, chiamare questo in onStart () eviterà invece che venga chiamato quando viene generata una semplice finestra di dialogo, ad esempio da un allarme che si attiva.
Sky Kelsey,

2

Risposta corretta qui

Crea una classe con il nome MyApp come di seguito:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private Context context;
    public void setContext(Context context)
    {
        this.context = context;
    }

    private boolean isInBackground = false;

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {


            isInBackground = true;
            Log.d("status = ","we are out");
        }
    }


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

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

            isInBackground = false;
            Log.d("status = ","we are in");
        }

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

Quindi, ovunque tu voglia (migliore prima attività avviata in app), aggiungi il codice qui sotto:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

Fatto! Ora quando l'app è in background, otteniamo il registro status : we are out e quando andiamo nell'app, otteniamo il registrostatus : we are out


1

La mia soluzione è stata ispirata dalla risposta di @ d60402 e si basa anche su una finestra temporale, ma non usando Timer:

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

  @Override
  protected void onStart() {
    super.onStart();
    wasInBackground = getApp().isInBackground;
    getApp().isInBackground = false;
    getApp().lastForegroundTransition = System.currentTimeMillis();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
      getApp().isInBackground = true;
  }

  protected SingletonApplication getApp(){
    return (SingletonApplication)getApplication();
  }
}

dove SingletonApplicationè un'estensione di Applicationclasse:

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}

1

Lo stavo usando con Google Analytics EasyTracker e ha funzionato. Potrebbe essere esteso per fare ciò che cerchi usando un semplice numero intero.

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

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

    private void appBackgroundedDetector() {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStart(activity);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                isAppBackgrounded++;
                if (isAppBackgrounded > 0) {
                    // Do something here
                }
            }

            @Override
            public void onActivityPaused(Activity activity) {
                isAppBackgrounded--;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStop(activity);
            }

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

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

1

so che è un po 'tardi ma penso che tutte queste risposte abbiano dei problemi mentre l'ho fatto come sotto e che funziona perfettamente.

creare un callback del ciclo di vita delle attività in questo modo:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

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

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    Activity lastActivity;
    @Override
    public void onActivityResumed(Activity activity) {
        //if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
        if (activity != null && activity == lastActivity) 
        {
            Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
        }

        lastActivity = activity;
    }

    @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) {

    }
}

e registralo sulla tua classe di applicazione come di seguito:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}

Questo viene chiamato continuamente per ogni attività. Come posso utilizzarlo se, ad esempio, voglio rilevare lo stato online dell'utente
Maksim Kniazev,

questo è quello che vuole la domanda. viene chiamato solo quando vai alla schermata principale e torni a qualsiasi attività.
Amir Ziarati,

se intendi la connettività Internet, penso sia meglio verificarlo quando ne hai bisogno. se hai bisogno di chiamare un API controlla la connessione Internet appena prima di chiamare.
Amir Ziarati,

1

Questa sembra essere una delle domande più complicate in Android poiché (al momento della stesura di questo documento) Android non ha equivalenti iOS applicationDidEnterBackground()o applicationWillEnterForeground()callback. Ho usato una libreria AppState che è stata messa insieme da @jenzz .

[AppState è] una libreria Android semplice e reattiva basata su RxJava che monitora le modifiche allo stato delle app. Notifica agli abbonati ogni volta che l'app passa in background e torna in primo piano.

Si è scoperto che questo è esattamente ciò di cui avevo bisogno, soprattutto perché la mia app aveva più attività, quindi il semplice controllo onStart()o onStop()un'attività non lo avrebbe tagliato.

Per prima cosa ho aggiunto queste dipendenze a Gradle:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

Quindi è stata una semplice questione di aggiungere queste righe in una posizione appropriata nel tuo codice:

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
    @Override
    public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
        switch (appState) {
            case FOREGROUND:
                Log.i("info","App entered foreground");
                break;
            case BACKGROUND:
                Log.i("info","App entered background");
                break;
        }
    }
});

A seconda di come ti iscrivi all'osservabile, potresti dover annullare l'iscrizione per evitare perdite di memoria. Ancora una volta maggiori informazioni sulla pagina di github .


1

Questa è la versione modificata della risposta di @ d60402: https://stackoverflow.com/a/15573121/4747587

Fai tutto quanto menzionato lì. Ma invece di avere un Base Activitye renderlo come genitore per ogni attività e la sostituzione di onResume()e onPause, fai quanto segue:

Nella tua classe di applicazione, aggiungi la riga:

registerActivityLifecycleCallbacks (callback Application.ActivityLifecycleCallbacks);

Questo callbackha tutti i metodi del ciclo di vita delle attività e ora puoi sovrascrivere onActivityResumed()e onActivityPaused().

Dai un'occhiata a questo Gist: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b


1

Puoi farlo facilmente con l'aiuto di ActivityLifecycleCallbackse ComponentCallbacks2qualcosa di simile di seguito.

Crea una classe AppLifeCycleHandlerimplementando sopra dette interfacce.

package com.sample.app;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;

/**
 * Created by Naveen on 17/04/18
 */
public class AppLifeCycleHandler
    implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

  AppLifeCycleCallback appLifeCycleCallback;

  boolean appInForeground;

  public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
    this.appLifeCycleCallback = appLifeCycleCallback;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    if (!appInForeground) {
      appInForeground = true;
      appLifeCycleCallback.onAppForeground();
    }
  }

  @Override
  public void onTrimMemory(int i) {
    if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      appInForeground = false;
      appLifeCycleCallback.onAppBackground();
    }
  }

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

  }

  @Override
  public void onActivityStarted(Activity activity) {

  }

  @Override
  public void onActivityPaused(Activity activity) {

  }

  @Override
  public void onActivityStopped(Activity activity) {

  }

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

  }

  @Override
  public void onActivityDestroyed(Activity activity) {

  }

  @Override
  public void onConfigurationChanged(Configuration configuration) {

  }

  @Override
  public void onLowMemory() {

  }

  interface AppLifeCycleCallback {

    void onAppBackground();

    void onAppForeground();
  }
}

Nella tua classe che estende Applicationimplementa AppLifeCycleCallbackper ottenere i callback quando l'app passa tra primo piano e sfondo. Qualcosa come sotto.

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

    @Override
    public void onCreate() {
        super.onCreate();
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        Log.d("LifecycleEvent", "onAppBackground");
    }

    @Override
    public void onAppForeground() {
        Log.d("LifecycleEvent", "onAppForeground");
    }
}

Spero che sia di aiuto.

MODIFICA In alternativa è ora possibile utilizzare il componente di architettura sensibile al ciclo di vita.


1

Dato che non ho trovato alcun approccio, che gestisca anche la rotazione senza controllare i timestamp, ho pensato di condividere anche come lo facciamo ora nella nostra app. L'unica aggiunta a questa risposta https://stackoverflow.com/a/42679191/5119746 è che prendiamo in considerazione anche l'orientamento.

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

   private var mAppIsInBackground = false
   private var mCurrentOrientation: Int? = null
   private var mOrientationWasChanged = false
   private var mResumed = 0
   private var mPaused = 0

Quindi, per i callback abbiamo prima il curriculum:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

E onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

E poi, ecco l'aggiunta: Verifica dei cambiamenti di orientamento:

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

Questo è tutto. Spero che questo aiuti qualcuno :)


1

Possiamo espandere questa soluzione usando LiveData:

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

    private var lifecycleListener: LifecycleObserver? = null

    override fun onActive() {
        super.onActive()
        lifecycleListener = AppLifecycleListener().also {
            ProcessLifecycleOwner.get().lifecycle.addObserver(it)
        }
    }

    override fun onInactive() {
        super.onInactive()
        lifecycleListener?.let {
            this.lifecycleListener = null
            ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
        }
    }

    internal inner class AppLifecycleListener : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onMoveToForeground() {
            value = State.FOREGROUND
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onMoveToBackground() {
            value = State.BACKGROUND
        }
    }

    enum class State {
        FOREGROUND, BACKGROUND
    }
}

Ora possiamo iscriverci a questo LiveData e catturare gli eventi necessari. Per esempio:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}

0

Queste risposte non sembrano corrette. Questi metodi vengono anche chiamati quando un'altra attività inizia e finisce. Quello che puoi fare è mantenere una bandiera globale (sì, i globi sono cattivi :) e impostarlo su true ogni volta che inizi una nuova attività. Impostalo su false nell'onCreate di ogni attività. Quindi, in onPause, controlli questa bandiera. Se è falso, l'app sta andando in background o viene uccisa.


Non ho parlato di un database ... cosa intendi?
Joris Weimar,

sto supportando la tua risposta. anche se possiamo salvare quel valore di flag nel database durante una chiamata di pausa non è la soluzione giusta ..
Sandeep P
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.