Verifica se un'applicazione Android è in esecuzione in background


329

Per sfondo, intendo che nessuna delle attività dell'applicazione è attualmente visibile all'utente?



7
Sono confuso qui ... perché Android non può fornire un semplice override sulla classe di applicazione per questo? È troppo difficile saperlo a livello di piattaforma? @Override protetto void onApplicationSentToBackground () {}
Chuck D

2
@ChuckD - avrebbe senso, che a volte sembra che l'SDK di Android voglia evitare di fare. : /
Segna il


1
iOS ha questo in serbo, non so perché Google lo renda così difficile. È un bisogno così ovvio.
Jerry Destremps,

Risposte:


388

Esistono pochi modi per rilevare se l'applicazione è in esecuzione in background, ma solo uno di questi è completamente affidabile:

  1. La soluzione giusta (crediti vanno a Dan , CommonsWare e NeTeInStEiN )
    la visibilità traccia della vostra applicazione da soli utilizzando Activity.onPause, Activity.onResumemetodi. Memorizza lo stato di "visibilità" in un'altra classe. Le buone scelte sono l'implementazione personale di Applicationo a Service(ci sono anche alcune varianti di questa soluzione se si desidera controllare la visibilità delle attività dal servizio).
     
    Esempio
    Implementare la Applicationclasse personalizzata (notare il isActivityVisible()metodo statico):

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

    Registra la tua classe di applicazione in AndroidManifest.xml:

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

    Aggiungi onPausee onResumea tutti Activitynel progetto (puoi creare un antenato comune per le tue attività, se lo desideri, ma se la tua attività è già estesa da MapActivity/ ListActivityecc., Devi comunque scrivere a mano quanto segue):

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

     
    Aggiornamento
    ActivityLifecycleCallbacks sono stati aggiunti nel livello API 14 (Android 4.0). Puoi usarli per tracciare se un'attività della tua applicazione è attualmente visibile all'utente. Controlla la risposta di Cornstalks qui sotto per i dettagli.

  2. Quello sbagliato che
    ho usato per suggerire la seguente soluzione:

    È possibile rilevare l'applicazione attualmente in primo piano / in background con la ActivityManager.getRunningAppProcesses()quale restituisce un elenco di RunningAppProcessInforecord. Per determinare se l'applicazione si trova nel RunningAppProcessInfo.importancecampo di controllo in primo piano per l'uguaglianza a RunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhile RunningAppProcessInfo.processNameè uguale al nome del pacchetto dell'applicazione.

    Inoltre, se chiami ActivityManager.getRunningAppProcesses()dal thread dell'interfaccia utente dell'applicazione, tornerà importante IMPORTANCE_FOREGROUNDper l'attività, indipendentemente dal fatto che sia effettivamente in primo piano o meno. Chiamalo nel thread in background (ad esempio via AsyncTask) e restituirà risultati corretti.

    Mentre questa soluzione può funzionare (e in effetti funziona la maggior parte delle volte), consiglio vivamente di astenermi dall'utilizzarla. Ed ecco perché. Come scrisse Dianne Hackborn :

    Queste API non sono lì per le applicazioni su cui basare il loro flusso di interfaccia utente, ma per fare cose come mostrare all'utente le app in esecuzione, o un task manager o simili.

    Sì, esiste un elenco in memoria per queste cose. Tuttavia, è disattivato in un altro processo, gestito da thread eseguiti separatamente dal tuo e non qualcosa su cui puoi contare (a) vedere in tempo per prendere la decisione corretta o (b) avere un'immagine coerente al momento del tuo ritorno. Inoltre, la decisione su quale sia la "prossima" attività da fare viene sempre presa nel punto in cui deve avvenire l'interruttore, e non è fino a quel punto esatto (dove lo stato dell'attività viene brevemente bloccato per fare l'interruttore) che noi in realtà sapere con certezza quale sarà la prossima cosa.

    E l'implementazione e il comportamento globale qui non sono garantiti per rimanere gli stessi in futuro.

    Vorrei averlo letto prima di pubblicare una risposta sulla comunicazione degli addebiti, ma spero che non sia troppo tardi per ammettere il mio errore.

  3. Un'altra soluzione sbagliata libreria
    Droid-Fu menzionata in una delle risposte utilizza ActivityManager.getRunningTasksper il suo isApplicationBroughtToBackgroundmetodo. Vedi il commento di Dianne sopra e non usare neanche quel metodo.


4
Per sapere se hai premuto il pulsante Home o qualche altra app ha guadagnato il focus: 1) implementa la buona soluzione . 2) In OnStoprichiesta a isActivityVisible.
Brais Gabin,

28
Sfortunatamente la tua soluzione "corretta" non funziona per me. Considera di scorrere le attività all'interno della tua app. Quello che succede allora è che il tuo flag 'inForeground' va così: Vero, Falso (tra 1a attività in pausa e 2a attività in ripresa) quindi di nuovo vero, ecc. Avresti quindi bisogno di un'isteresi di qualche tipo.
Radu,

14
Questa soluzione non funziona se non puoi controllare direttamente tutte le attività. Ad esempio, se hai un'attività da un sdk di terze parti o addirittura lanci un intento ACTION_VIEW.
user123321

66
Android è un tale disastro. Nessuno pensava che qualcuno potesse voler conservare i dati a livello di app? Dammi una pausa

8
Sembra che la vera risposta a questa domanda sia "Non puoi controllarla correttamente". La soluzione cosiddetta 'corretta' è al massimo una soluzione alternativa, quindi sta usando ActivityLifecycleCallbacks. È ancora necessario considerare il passaggio tra le attività che verrebbero registrate come "non in primo piano". Mi viene in mente che non puoi controllare una cosa semplice come quella ...
serina,

263

NON UTILIZZARE QUESTA RISPOSTA

La risposta di user1269737 è il modo corretto (approvato da Google / Android) per farlo . Vai a leggere la loro risposta e dai loro un +1.

Lascio la mia risposta originale qui per l'amor dei posteri. Questo era il migliore disponibile nel 2012, ma ora Android ha il supporto adeguato per questo.

Risposta originale

La chiave sta utilizzando ActivityLifecycleCallbacks(si noti che ciò richiede l'API Android livello 14 (Android 4.0)). Basta controllare se il numero di attività interrotte è uguale al numero di attività avviate. Se sono uguali, l'applicazione è in background. Se ci sono più attività avviate, l'applicazione è ancora visibile. Se vengono ripristinate più attività che in pausa, l'applicazione non è solo visibile, ma è anche in primo piano. Ci sono 3 stati principali in cui la tua attività può essere, quindi: visibile e in primo piano, visibile ma non in primo piano, e non visibile e non in primo piano (cioè in background).

La cosa davvero bella di questo metodo è che non ha i problemi asincroni getRunningTasks(), ma non è necessario modificare tutti Activitynella tua applicazione per impostare / disinserire qualcosa in onResumed()/ onPaused(). Sono solo poche righe di codice che sono autonome e funzionano in tutta la tua applicazione. Inoltre, non sono richieste autorizzazioni funky.

MyLifecycleHandler.java:

public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
    // I use four separate variables here. You can, of course, just use two and
    // increment/decrement them instead of using four and incrementing them all.
    private int resumed;
    private int paused;
    private int started;
    private int stopped;

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        ++paused;
        android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
    }

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

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
    }

    @Override
    public void onActivityStopped(Activity activity) {
        ++stopped;
        android.util.Log.w("test", "application is visible: " + (started > stopped));
    }

    // If you want a static function you can use to check if your application is
    // foreground/background, you can use the following:
    /*
    // Replace the four variables above with these four
    private static int resumed;
    private static int paused;
    private static int started;
    private static int stopped;

    // And these two public static functions
    public static boolean isApplicationVisible() {
        return started > stopped;
    }

    public static boolean isApplicationInForeground() {
        return resumed > paused;
    }
    */
}

MyApplication.java:

// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        // Simply add the handler, and that's it! No need to add any code
        // to every activity. Everything is contained in MyLifecycleHandler
        // with just a few lines of code. Now *that's* nice.
        registerActivityLifecycleCallbacks(new MyLifecycleHandler());
    }
}

@Mewzer ha posto alcune buone domande su questo metodo a cui vorrei rispondere in questa risposta per tutti:

onStop()non viene chiamato in situazioni di memoria insufficiente; è un problema qui?

No. I documenti per onStop()dire:

Si noti che questo metodo non può mai essere chiamato, in situazioni di memoria insufficiente in cui il sistema non dispone di memoria sufficiente per mantenere in esecuzione il processo dell'attività dopo che è stato chiamato il metodo onPause ().

La chiave qui è "mantieni attivo il processo della tua attività ..." Se viene mai raggiunta questa situazione di memoria insufficiente, il tuo processo viene effettivamente ucciso (non solo la tua attività). Ciò significa che questo metodo di controllo dello sfondo è ancora valido perché a) non è possibile verificare comunque lo sfondo se il processo viene interrotto eb) se il processo viene riavviato (perché viene creata una nuova attività), il membro le variabili (statiche o meno) per MyLifecycleHandlerverranno reimpostate su 0.

Funziona con le modifiche alla configurazione?

Per impostazione predefinita, no. Devi impostare esplicitamente configChanges=orientation|screensize( |con qualsiasi altra cosa tu voglia) nel tuo file manifest e gestire le modifiche alla configurazione, altrimenti la tua attività verrà distrutta e ricreata. Se non si imposta questa, i metodi del vostro attività saranno chiamati in questo ordine: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. Come puoi vedere, non ci sono sovrapposizioni (normalmente due attività si sovrappongono molto brevemente quando si passa da una all'altra, ed è così che funziona questo metodo di rilevamento dello sfondo). Per ovviare a questo, devi impostare in configChangesmodo che la tua attività non venga distrutta. Per fortuna, ho dovuto impostareconfigChangesgià in tutti i miei progetti perché non era auspicabile che l'intera mia attività venisse distrutta sullo schermo ruotando / ridimensionando, quindi non ho mai trovato questo problematico. (grazie a dpimka per rinfrescare la mia memoria su questo e correggermi!)

Una nota:

Quando ho detto "sfondo" qui in questa risposta, intendevo "la tua app non è più visibile". Le attività Android possono essere visibili ma non in primo piano (ad esempio, se è presente un overlay di notifica trasparente). Ecco perché ho aggiornato questa risposta per riflettere ciò.

È importante sapere che Android ha uno strano momento di limbo quando si cambia attività in cui nulla è in primo piano . Per questo motivo, se si controlla se l'applicazione è in primo piano quando si passa da un'attività all'altra (nella stessa app), ti verrà comunicato che non sei in primo piano (anche se l'app è ancora l'app attiva ed è visibile ).

È possibile verificare se la vostra app è in primo piano nella vostra Activity's onPause()metodo , dopo super.onPause() . Ricorda solo lo strano stato di limbo di cui ho appena parlato.

È possibile verificare se la vostra applicazione è visibile (cioè se non è in background) nel vostro Activity's onStop()metodo dopo super.onStop() .


1
Sembra interessante, ma cosa succede in situazioni di memoria insufficiente? Non è garantito che verrà chiamato onStop (). Potremmo mai entrare nella situazione in cui un onStop () non viene chiamato e il contatore arrestato non viene incrementato - questo significa che il controllo in background non è più affidabile? O questo non accadrà mai?
Mewzer,

1
Inoltre, questo ignorerà le modifiche alla configurazione? Oppure l'applicazione verrà considerata in background se un'attività viene ricreata a seguito di una modifica della configurazione (ad esempio, Modifica dell'orientamento) ?. Ci dispiace, per le domande, ma penso che tu sia interessato a qualcosa e sono interessato a sapere se funziona in questi casi limite.
Mewzer,

1
@Mewzer: Stavo per rispondere come commento, ma ci vorrà un po 'di battitura per ottenere queste risposte, quindi controlla tra qualche minuto e modificherò la mia risposta.
Cornstalks,

1
@Mewzer: dovresti trovare le tue risposte ora. Fammi sapere se ci sono altre domande!
Cornstalks,

2
@Mewzer: ho appena aggiunto una nota che potrebbe interessarti. In particolare, controlla lo sfondo in onStop()seguito super.onStop(). Non controllare lo sfondo in onPause().
Cornstalks,

187

SOLUZIONE GOOGLE - non un hack, come le soluzioni precedenti. Usa ProcessLifecycleOwner

Kotlin:

class ArchLifecycleApp : Application(), LifecycleObserver {

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onAppForegrounded() {
        // App in foreground
    }

}


Giava:

public class ArchLifecycleApp extends Application implements LifecycleObserver {

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

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppForegrounded() {
        // App in foreground
    }
}

in app.gradle

dependencies {
    ...
    implementation "android.arch.lifecycle:extensions:1.1.0"

    //New Android X dependency is this - 
    implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

}

allprojects {
    repositories {
        ...
        google()
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

Puoi leggere ulteriori informazioni sui componenti dell'architettura relativi al ciclo di vita qui - https://developer.android.com/topic/libraries/architecture/lifecycle


10
Questa dovrebbe sicuramente essere la risposta corretta! Funzionava come un incantesimo: D
JaviOverflow dal

2
Funziona perfettamente, ho anche modificato un po 'in modo da poter accedere più facilmente allo stato di primo piano / sfondo al di fuori di questa classe: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }quindi puoi ottenere lo stato di primo piano conArchLifecycleApp.isForeground()
Jose Jet,

2
Oh amico, è molto meglio della mia vecchia risposta. Prendi un +1 da me. Ho aggiornato la mia risposta per indirizzare le persone alla tua.
Cornstalks,

2
Sebbene questa sia una risposta corretta, non è necessario implementare i callback, è possibile eseguire una query su ProcessLifecycleOwner ogni volta che lo si desidera. Verificare stackoverflow.com/a/52678290/6600000
Keivan Esbati

2
Come dice doc The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , questo non funziona per le multiple processesapp, c'è qualche api che possiamo ottenere elegantemente?
acntwww

23

A partire dalla libreria di supporto versione 26 è possibile utilizzare ProcessLifecycleOwner , basta aggiungerlo alla propria dipendenza come descritto qui , ad esempio:

dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData).
    //     Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

E poi basta interrogare ProcessLifecycleOwnerogni volta che vuoi per lo stato dell'app, esempi:

//Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;

//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);

2
Grazie amico, questo è il modo migliore e più semplice che è adatto in qualsiasi parte del tuo codice specialmente quando usi fcm.
Mihae Kheel,

se l'applicazione viene chiusa completamente, quale sarebbe il ritorno del primo metodo?
Evgeniy Mishustin,

@EvgeniyMishustin dipende dallo stato corrente dell'applicazione, ma di solito vedrai CREATO quindi DISTRUTTO e, successivamente, non riceverai nuovi eventi.
Keivan Esbati,

Quindi dov'è qualsiasi istruzione "IF" per vedere se l'app è in background (in primo piano) ???
Ekashking

@ekashking ha appena inserito l'intera istruzione nella clausola if. Ad esempio: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => L'app è in primo piano
Keivan Esbati

20

Dall'API 16 di Android esiste un modo semplice per verificare se l'app è in primo piano. Potrebbe non essere infallibile, ma nessun metodo su Android è infallibile. Questo metodo è abbastanza buono da usare quando il servizio riceve l'aggiornamento dal server e deve decidere se mostrare la notifica oppure no (perché se l'interfaccia utente è in primo piano, l'utente noterà l'aggiornamento senza notifica).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

questo codice deve rientrare nella classe di servizio o in un'altra classe, ad es. classe di applicazione? Molte grazie.
Woppi,

.. ovunque tu voglia usarlo come ultima riga è solo un valore booleano che dovresti controllare.
AO_

Questa è la stessa metodologia dell'SDK Android AWS per le notifiche push.
spakmad,

Tenere presente che "La definizione di sfondo ai fini delle limitazioni del servizio è distinta dalla definizione utilizzata dalla gestione della memoria; un'app potrebbe essere in background per quanto riguarda la gestione della memoria, ma in primo piano per quanto riguarda la sua capacità di avviare servizi.) " developer.android.com/about/versions/oreo/background.html (
ARLabs

Grazie, ha funzionato! Sono stato in grado di utilizzare questo codice in a JobServiceper rilevare che il servizio è in esecuzione in background.
Michael Osofsky,

17

La risposta di Idolon è soggetta a errori e molto più complicata anche se ripeti qui controllare che l'applicazione Android sia in primo piano o no? e qui Determinare l'applicazione di primo piano corrente da un'attività o un servizio in background

Esiste un approccio molto più semplice:

Su un'attività di base estesa da tutte le attività:

protected static boolean isVisible = false;

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


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

Ogni volta che è necessario verificare se una delle attività dell'applicazione è in primo piano, basta controllare isVisible() ;

Per comprendere questo approccio, controllare questa risposta del ciclo di vita delle attività side-by-side : Ciclo di vita delle attività side-by-side


3
Idolon's answer is error prone- purtroppo devo essere d'accordo con te. Sulla base del commento di Dianne Hackborn nei gruppi di Google ho aggiornato la mia risposta. Controlla per favore per i dettagli.
Idolon,

2
Anche questa non è una soluzione infallibile. Uno scenario è se l'utente tirato giù il pannello di notifica, quindi né la onPause, onStopné l' onResumeevento si chiama. Quindi cosa fai se nessuno di questi eventi viene generato ?!

Questo mi ha portato a questa domanda: stackoverflow.com/questions/33657102/…
Ruchir Baronia,

Purtroppo, ma questo codice funziona in modo errato quando viene avviata un'attività quando uno schermo è spento. In questo caso onResume e onPause sono chiamati making isVisible = false.
CoolMind,

@CoolMind Puoi spiegare qual è il caso d'uso in cui avvieresti un'attività mentre sei in background?
neteinstein,

11

Ho provato la soluzione consigliata che utilizza Application.ActivityLifecycleCallbacks e molti altri, ma non hanno funzionato come previsto. Grazie a Sarge , ho trovato una soluzione abbastanza semplice e diretta che sto descrivendo di seguito.

La chiave della soluzione è il fatto di capire che se abbiamo ActivityA e ActivityB e chiamiamo ActivityB da ActivityA (e non chiamiamo ActivityA.finish), allora ActivityB onStart()verrà chiamato prima di ActivityA onStop().

Questa è anche la principale differenza tra onStop()e onPause()che nessuno ha menzionato negli articoli che ho letto.

Quindi, in base al comportamento del ciclo di vita di questa attività, puoi semplicemente contare quante volte ha fatto onStart()e onPause()ricevuto una chiamata nel tuo programma. Si noti che per ciascun Activity programma, è necessario eseguire l'override onStart()e onStop(), al fine di incrementare / decrementare la variabile statica utilizzata per il conteggio. Di seguito è riportato il codice che implementa questa logica. Nota che sto usando una classe che si estende Application, quindi non dimenticare di dichiarare Manifest.xmlall'interno del tag Application:, android:name=".Utilities"sebbene possa essere implementato anche usando una semplice classe personalizzata.

public class Utilities extends Application
{
    private static int stateCounter;

    public void onCreate()
    {
        super.onCreate();
        stateCounter = 0;
    }

    /**
     * @return true if application is on background
     * */
    public static boolean isApplicationOnBackground()
    {
        return stateCounter == 0;
    }

    //to be called on each Activity onStart()
    public static void activityStarted()
    {
        stateCounter++;
    }

    //to be called on each Activity onStop()
    public static void activityStopped()
    {
        stateCounter--;
    }
}

Ora su ogni attività del nostro programma, dobbiamo ignorare onStart()e onStop()e incremento / decremento come illustrato di seguito:

@Override
public void onStart()
{
    super.onStart();
    Utilities.activityStarted();
}

@Override
public void onStop()
{
    Utilities.activityStopped();
    if(Utilities.isApplicationOnBackground())
    {
        //you should want to check here if your application is on background
    }
    super.onStop();
}

Con questa logica, ci sono 2 possibili casi:

  1. stateCounter = 0 : Il numero di arresti è uguale al numero di attività avviate, il che significa che l'applicazione è in esecuzione in background.
  2. stateCounter > 0 : Il numero di avviati è maggiore del numero di arrestato, il che significa che l'applicazione è in esecuzione in primo piano.

Avviso: stateCounter < 0significherebbe che ci sono attività più interrotte anziché avviate, il che è impossibile. Se incontri questo caso, significa che non stai aumentando / diminuendo il contatore come dovresti.

Sei pronto per andare. Dovresti controllare se la tua applicazione è in background all'interno onStop().


Mi piacerebbe spostare if(Utilities.isApplicationOnBackground()) …a Utilities. Perché altrimenti solo un'attività specifica reagirà all'evento.
Visualizza nome

10

Non c'è modo, a parte te, di seguirlo da solo, per determinare se una qualsiasi delle tue attività è visibile o meno. Forse dovresti prendere in considerazione la possibilità di porre una nuova domanda StackOverflow, spiegando cosa stai cercando di ottenere da un'esperienza utente, così possiamo forse darti idee di implementazione alternative.


2
In Android, abbiamo un'impostazione chiamata "Dati di sfondo". Questa impostazione disattiva qualsiasi connessione dati in background quando l'applicazione è in esecuzione in background. Voglio implementare l'opzione "Dati di sfondo" per la mia applicazione, quindi quando nessuna delle mie attività è visibile all'utente, vorrei che il mio servizio smettesse di eseguire qualsiasi trasferimento di dati, ma nel momento in cui riprende una delle mie attività, vorrei riprendere il trasferimento dei dati
cppdev

1
@cppdev: Speriamo che il "trasferimento di dati" sia condotto da a Service. In tal caso, fai in modo che le tue attività notifichino il servizio quando appaiono e scompaiono. Se il Serviceparametro determina che non sono visibili attività e rimane tale per un certo periodo di tempo, interrompere il trasferimento dei dati al successivo punto logico di arresto. Sì, ciò richiederà il codice per ciascuna delle tue attività, ma al momento è inevitabile AFAIK.
CommonsWare

1
Se si desidera evitare di copiare e incollare il codice comune tra tutte le attività, è possibile creare una classe che MyActivityClasseredita Activitye implementa i metodi del ciclo di vita e che eredita tutte le attività MyActivityClass. Questo non funzionerà PreferenceActivityo MapActivitypenserà (vedi questa domanda )
Guillaume Brunerie,

@CommonsWare avevo provato con OnPause () OnResume () che è attivo o meno, ma se la mia app non viene visualizzata nella schermata di visualizzazione se funziona in background, controlla se è attiva o meno
Manoj,

@CommonsWare avevo provato con OnPause () OnResume () che è attivo o meno, ma se la mia app non viene visualizzata nella schermata di visualizzazione se funziona in background, controlla se è attiva o meno
Manoj,

5

È possibile utilizzare ComponentCallbacks2 per rilevare se l'app è in background. A proposito, questo callback è disponibile solo in API Level 14 (Ice Cream Sandwich) e versioni successive.

Riceverai una chiamata al metodo:

public abstract void onTrimMemory (int level)

se il livello è ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENallora l'app è in background.

È possibile implementare questa interfaccia per una activity, servicee così via

public class MainActivity extends AppCompatActivity 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) {
        // app is in background
     }
   }
}

1
Ho provato la tua risposta ma non è così affidabile. il callback onTrimMemory non verrà attivato quando lo schermo è bloccato né quando si preme il pulsante "power" per bloccare lo schermo. Inoltre, non restituirà sempre TRIM_MEMORY_UI_HIDDEN se la tua app è visibile e apri un'altra app tramite una notifica sulla barra di stato. L'unica soluzione affidabile è implementare ActivityLifecycleCallbacks e adattarlo al proprio caso d'uso.
Velval,

4

Basandosi sulla risposta di @Cornstalks per includere un paio di utili funzioni.

Proprietà aggiuntive:

  • ha introdotto il modello singleton in modo da poterlo fare ovunque nell'applicazione: AppLifecycleHandler.isApplicationVisible () e AppLifecycleHandler.isApplicationInForeground ()
  • aggiunta gestione di eventi duplicati (vedi commenti // intraprendi qualche azione sul cambio di visibilità e // agisci sul cambio di in primo piano)

App.java

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
    }
}

AppLifecycleHandler.java

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
    private int resumed;
    private int started;

    private final String DebugName = "AppLifecycleHandler";

    private boolean isVisible = false;
    private boolean isInForeground = false;

    private static AppLifecycleHandler instance;

    public static AppLifecycleHandler getInstance() {
        if (instance == null) {
            instance = new AppLifecycleHandler();
        }

        return instance;
    }

    private AppLifecycleHandler() {
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
        android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

    @Override
    public void onActivityPaused(Activity activity) {
        --resumed;
        android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

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

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
        android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    @Override
    public void onActivityStopped(Activity activity) {
        --started;
        android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    private void setVisible(boolean visible) {
        if (isVisible == visible) {
            // no change
            return;
        }

        // visibility changed
        isVisible = visible;
        android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);

        // take some action on change of visibility
    }

    private void setForeground(boolean inForeground) {
        if (isInForeground == inForeground) {
            // no change
            return;
        }

        // in foreground changed
        isInForeground = inForeground;
        android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);

        // take some action on change of in foreground

    }

    public static boolean isApplicationVisible() {
        return AppLifecycleHandler.getInstance().started > 0;
    }

    public static boolean isApplicationInForeground() {
        return AppLifecycleHandler.getInstance().resumed > 0;
    }
}

3

La migliore soluzione che ho trovato utilizza i timer.

Hai avviato un timer in onPause () e annullato lo stesso timer in onResume (), esiste 1 istanza del timer (di solito definita nella classe Application). Il timer stesso è impostato per eseguire un Runnable dopo 2 secondi (o qualsiasi intervallo che ritieni appropriato), quando il timer si attiva imposta un flag che contrassegna l'applicazione come in background.

Nel metodo onResume () prima di annullare il timer, è possibile eseguire una query sul flag di sfondo per eseguire qualsiasi operazione di avvio (ad esempio avviare i download o abilitare i servizi di localizzazione).

Questa soluzione consente di avere diverse attività nello stack posteriore e non richiede autorizzazioni per l'implementazione.

Questa soluzione funziona bene se usi anche un bus eventi, poiché il tuo timer può semplicemente generare un evento e varie parti della tua app possono rispondere di conseguenza.


Sto iniziando a pensare che questa sia la migliore (anche se sfortunata) soluzione
dhaag23

Sì, questa è anche la soluzione migliore che ho gestito. Avevo bisogno di interrompere la scansione bluetooth quando l'app non era in primo piano, ma non potevo semplicemente usare in pausa o interrompere o distruggere perché non volevo fermarmi costantemente e iniziare quando l'utente navigava nell'app.
CaptRespect,

3

Se attivi le impostazioni dello sviluppatore "Non mantenere attività" - controlla che il conteggio delle attività create non sia sufficiente. È necessario verificare anche isSaveInstanceState . Il mio metodo personalizzato isApplicationRunning () check è l'app Android in esecuzione:

Ecco il mio codice di lavoro:

public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
    private int created;
    private boolean isSaveInstanceState;
    private static AppLifecycleService instance;

    private final static String TAG = AppLifecycleService.class.getName();

    public static AppLifecycleService getInstance() {
        if (instance == null) {
            instance = new AppLifecycleService();
        }
        return instance;
    }

    public static boolean isApplicationRunning() {
        boolean isApplicationRunning = true;
        if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
            isApplicationRunning = false;
        }
        return isApplicationRunning;
    }

    public static boolean isSaveInstanceState() {
        return AppLifecycleService.getInstance().isSaveInstanceState;
    }

    public static int getCountCreatedActvities() {
        return AppLifecycleService.getInstance().created;
    }

    private AppLifecycleService() {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        this.isSaveInstanceState = true;
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
        --created;
    }

    @Override
    public void onActivityResumed(Activity activity) {   }

    @Override
    public void onActivityPaused(Activity activity) { }


    @Override
    public void onActivityStarted(Activity activity) { }

    @Override
    public void onActivityStopped(Activity activity) { }        

}

3

L'unica soluzione corretta:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

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

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onAppBackgrounded() {
        // app in background
        if (mainActivity != null) {
            ...
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onAppForegrounded() {
        // app in foreground
        if (mainActivity != null) {
            ...
        }
    }

}

Non vedo come questa soluzione possa darmi una risposta a una semplice domanda all'interno di un'istruzione IF sulla mia attività (o frammento) se la mia app è in background o in primo piano. Dov'è l'istruzione "IF" ???
Ekashking

2

Per rispondere a ciò che hanno detto CommonsWare e Key, potresti forse estendere la classe Application e fare in modo che tutte le tue attività la chiamino sui loro metodi onPause / onResume. Ciò ti consentirebbe di sapere quali attività sono visibili, ma probabilmente potrebbero essere gestite meglio.

Puoi approfondire esattamente ciò che hai in mente? Quando dici di essere in esecuzione in background, intendi semplicemente avere l'applicazione ancora in memoria anche se non è attualmente sullo schermo? Hai considerato l'utilizzo dei servizi come un modo più persistente per gestire la tua app quando non è attiva?


In Android, abbiamo un'impostazione chiamata "Dati di sfondo". Questa impostazione disattiva qualsiasi connessione dati in background quando l'applicazione è in esecuzione in background. Voglio implementare l'opzione "Dati di sfondo" per la mia applicazione, quindi quando nessuna delle mie attività è visibile all'utente, vorrei che il mio servizio smettesse di eseguire qualsiasi trasferimento di dati, ma nel momento in cui riprende una delle mie attività, vorrei riprendere il trasferimento dei dati
cppdev

1
Applicationnon ha onPause()o onResume().
CommonsWare

1
@CommonsWare Hai ragione, mi riferivo ad ogni singola Attività che contattava l'Applicazione al momento della pausa / ripresa. Questa è fondamentalmente l'idea che hai appena condiviso sul commento alla tua risposta, sebbene tu abbia usato Servizi che immagino sia una mossa più intelligente.
Dan,

2

Ho realizzato la mia implementazione di ActivityLifecycleCallbacks. Sto usando SherlockActivity, ma per la normale classe Activity potrebbe funzionare.

Innanzitutto, sto creando un'interfaccia con tutti i metodi per tenere traccia del ciclo di vita delle attività:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

In secondo luogo, ho implementato questa interfaccia nella classe della mia applicazione:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

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

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

In terzo luogo, sto creando una classe che si estende da SherlockActivity:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityResumed(this);
        super.onResume();

    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityPaused(this);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityDestroyed(this);
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        nMyApplication.onActivityStarted(this);
        super.onStart();
    }

    @Override
    protected void onStop() {
        nMyApplication.onActivityStopped(this);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        nMyApplication.onActivitySaveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }   
}

In quarto luogo, tutta la classe che si estende da SherlockActivity, ho sostituito per MySherlockActivity:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

Ora, nel logcat vedrai i log programmati nell'implementazione dell'interfaccia realizzata in MyApplication.


1

L'attività viene messa in pausa quando viene visualizzata una finestra di dialogo, pertanto tutte le soluzioni consigliate sono mezze soluzioni. È necessario creare hook anche per le finestre di dialogo.



1

Documenti ufficiali:

Il sistema distingue tra app in primo piano e in background. (La definizione di sfondo ai fini delle limitazioni del servizio è distinta dalla definizione utilizzata dalla gestione della memoria; un'app potrebbe essere in background per quanto riguarda la gestione della memoria , ma in primo piano per quanto riguarda la sua capacità di avviare servizi.) Un'app è considerato in primo piano se si verifica una delle seguenti condizioni:

  1. Ha un'attività visibile, indipendentemente dal fatto che l'attività sia avviata o in pausa.
  2. Ha un servizio in primo piano.
  3. Un'altra app in primo piano è connessa all'app, vincolando uno dei suoi servizi o facendo uso di uno dei suoi fornitori di contenuti. Ad esempio, l'app è in primo piano se un'altra app si lega al suo:
    • IME
    • Servizio di carta da parati
    • Listener di notifica
    • Servizio vocale o di testo

Se nessuna di queste condizioni è vera, l'app viene considerata in background.


0

Un'altra soluzione per questo vecchio post (per quelli che potrebbe aiutare):


<application android:name=".BaseApplication" ... >

public class BaseApplication extends Application {

    private class Status {
        public boolean isVisible = true;
        public boolean isFocused = true;
    }

    private Map<Activity, Status> activities;

    @Override
    public void onCreate() {
        activities = new HashMap<Activity, Status>();
        super.onCreate();
    }

    private boolean hasVisibleActivity() {
        for (Status status : activities.values())
            if (status.isVisible)
                return true;
        return false;
    }

    private boolean hasFocusedActivity() {
        for (Status status : activities.values())
            if (status.isFocused)
                return true;
        return false;
    }

    public void onActivityCreate(Activity activity, boolean isStarting) {
        if (isStarting && activities.isEmpty())
            onApplicationStart();
        activities.put(activity, new Status());
    }

    public void onActivityStart(Activity activity) {
        if (!hasVisibleActivity() && !hasFocusedActivity())
            onApplicationForeground();
        activities.get(activity).isVisible = true;
    }

    public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
        activities.get(activity).isFocused = hasFocus;
    }

    public void onActivityStop(Activity activity, boolean isFinishing) {
        activities.get(activity).isVisible = false;
        if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
            onApplicationBackground();
    }

    public void onActivityDestroy(Activity activity, boolean isFinishing) {
        activities.remove(activity);
        if(isFinishing && activities.isEmpty())
            onApplicationStop();
    }

    private void onApplicationStart() {Log.i(null, "Start");}
    private void onApplicationBackground() {Log.i(null, "Background");}
    private void onApplicationForeground() {Log.i(null, "Foreground");}
    private void onApplicationStop() {Log.i(null, "Stop");}

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

    @Override
    protected void onCreate(Bundle state) {
        application = (BaseApplication) getApplication();
        application.onActivityCreate(this, state == null);
        super.onCreate(state);
    }

    @Override
    protected void onStart() {
        application.onActivityStart(this);
        super.onStart();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        application.onActivityWindowFocusChanged(this, hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    protected void onStop() {
        application.onActivityStop(this, isFinishing());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        application.onActivityDestroy(this, isFinishing());
        super.onDestroy();
    }

}

0

Vedi il commento nella funzione onActivityDestroyed.

Funziona con SDK target versione 14>:

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        active--;

        // if active var here ever becomes zero, the app is closed or in background
        if(active == 0){
            ...
        }

    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
        active++;
    }
}

0

È necessario utilizzare una preferenza condivisa per archiviare la proprietà e agire su di essa utilizzando il servizio di associazione delle attività. Se usi solo il binding (che non usa mai startService), il tuo servizio verrebbe eseguito solo quando ti legherai ad esso, (bind onResume e unbind onPause) che lo farebbe funzionare solo in primo piano, e se vuoi lavorare su in background è possibile utilizzare il servizio di avvio / arresto regolare.


0

Penso che questa domanda dovrebbe essere più chiara. Quando? Dove? Qual è la tua situazione specifica che vuoi sapere se la tua app è in background?

Ho appena presentato la mia soluzione a modo mio.
A tale scopo, utilizzo il campo "importanza" della RunningAppProcessInfoclasse nel onStopmetodo di ogni attività nella mia app, che può essere semplicemente ottenuto fornendo BaseActivityun'estensione per altre attività che implementa il onStopmetodo per verificare il valore di "importanza". Ecco il codice:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}

Questa non è una soluzione raccomandata come indicato nella risposta di @ Idolon.
CoolMind,

0

Consiglio di leggere questa pagina: http://developer.android.com/reference/android/app/Activity.html

In breve, la tua attività non è più visibile dopo che onStop()è stata chiamata.


3
Ho circa 10 attività nella mia applicazione. Quindi voglio sapere se nessuno di essi è visibile all'utente. In tutto, voglio sapere se la mia applicazione nel suo insieme è in esecuzione in background
cppdev

Quindi tieni traccia di tutti i 10 ish. Oppure, come suggerito da CommonsWare, spiega cosa stai cercando di fare.
Chiave

3
Questo non è corretto La tua attività è visibile fino al onStop; tra onPauseed onStopè visibile , ma non in primo piano .
Nickgrim,

@nickgrim: cosa non è corretto? Ho affermato che un'attività non è più visibile dopo la onStop()chiamata, che è in linea con ciò che hai scritto.
Key

@Key: originariamente hai detto fino a quando non onPauseviene chiamato: una modifica recente ti ha corretto.
Nickgrim,

0

Che dire dell'utilizzo di getApplicationState (). IsInForeground ()?


0

A mio avviso, molte risposte introducono un pesante carico di codice e portano molta complessità e non leggibilità.

Quando le persone chiedono a SO come comunicare tra a Servicee a Activity, di solito consiglio di usare LocalBroadcastManager .


Perché?

Bene, citando i documenti:

  • Sai che i dati che stai trasmettendo non lasceranno la tua app, quindi non devi preoccuparti di perdere dati privati.

  • Non è possibile per altre applicazioni inviare queste trasmissioni alla tua app, quindi non devi preoccuparti di avere falle di sicurezza che possono sfruttare.

  • È più efficiente dell'invio di una trasmissione globale attraverso il sistema.

Non nei documenti:

  • Non richiede librerie esterne
  • Il codice è minimo
  • È veloce da implementare e capire
  • Nessun callback auto-implementato personalizzato / modello ultra-singleton / intra-process qualunque ...
  • Non ci sono forti riferimenti su Activity, Application, ...

Descrizione

Quindi, vuoi controllare se qualcuno dei Activityè attualmente in primo piano. Di solito lo fai in a Service, o tuoApplication classe.

Questo significa che i tuoi Activityoggetti diventano il mittente di un segnale (sono acceso / spento). Il tuo Service, d'altra parte, diventa ilReceiver .

Ci sono due momenti in cui il tuoActivity dice se sta andando in primo piano o sullo sfondo (sì, solo due ... non 6).

Quando il Activitydiventa in primo piano, onResume()viene attivato il metodo (chiamato anche dopo onCreate()).

Quando il Activityva nella parte posteriore, onPause()viene chiamato.

Questi sono i momenti in cui Activitydovresti inviare il segnale al tuoService per descriverne lo stato.

In caso di multipli Activity, ricordare il anActivity entra in secondo piano, poi ne viene in primo piano un altro.

Quindi la situazione sarebbe: *

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

Il Service/ Applicationcontinuerà semplicemente ad ascoltare quei segnali e agirà di conseguenza.


Codice (TLDR)

È Servicenecessario implementare un BroadcastReceiverper ascoltare i segnali.

this.localBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // received data if Activity is on / off
    }
}

public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 

Registrare il ReceiverinService::onCreate()

@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Annulla la registrazione Service::onDestroy()

@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

Ora i tuoi Activitydevono comunicare il loro stato.

Nel Activity::onResume()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Nel Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Una situazione molto, molto comune

Sviluppatore: Voglio inviare dati dal mio Servicee aggiornare il Activity. Come posso verificare se Activityè in primo piano?

Di solito non è necessario verificare se Activityè in primo piano o meno. Invia i dati via LocalBroadcastManagerdal tuo Service. Se Activityè attivo, allora risponderà e agirà.

Per questa situazione molto comune, Servicediventa il mittente e Activityattua il BroadcastReceiver.

Quindi, crea un Receivernel tuo Activity. Registrati onResume()e annulla la registrazione onPause(). Non è necessario utilizzare gli altri metodi del ciclo di vita .

Definire il Receivercomportamento in onReceive()(aggiornare ListView, farlo, farlo, ...).

In questo modo Activityascolterà solo se è in primo piano e non succederà nulla se è nella parte posteriore o viene distrutto.

In caso di multipli Activity, qualunque Activitysia attivo risponderà (se implementano anche il Receiver).

Se tutti sono in background, nessuno risponderà e il segnale andrà semplicemente perso.

Invia i dati da Servicevia Intent(vedi codice sopra) specificando l'ID del segnale.



0
fun isAppInForeground(): Boolean {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false

    val appProcesses = activityManager.runningAppProcesses ?: return false

    val packageName = packageName
    for (appProcess in appProcesses) {
        if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
            return true
        }
    }

    return false
}

0

Nessuna delle risposte è adatta al caso specifico se si cerca di sapere se un'attività specifica è in primo piano e se si è un SDK senza accesso diretto all'applicazione. Per me ero in thread in background avendo appena ricevuto una notifica push per un nuovo messaggio di chat e volevo visualizzare una notifica di sistema solo se la schermata della chat non è in primo piano.

Usando ActivityLifecycleCallbacksquello come raccomandato in altre risposte, ho creato una piccola classe util che ospita la logica se MyActivityè in primo piano o meno.

class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {

private var isMyActivityInForeground = false

init {
    (context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}

fun isMyActivityForeground() = isMyActivityInForeground

override fun onActivityPaused(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = false
    }
}

override fun onActivityResumed(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = true
    }
}

}


-1

Nelle mie attività su Resume e su Pausa scrivo un booleano isVisible su SharedPrefences.

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

E leggilo altrove quando necessario tramite,

    // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    if(!sharedPrefs.getBoolean("visible", true)){...}

Forse non è elegante, ma funziona per me ...


-1

Potrebbe essere troppo tardi per rispondere, ma se qualcuno viene a trovarci, ecco la soluzione che suggerisco, il motivo (i) in cui un'app vuole sapere che lo stato di essere in background o in primo piano può essere molti, alcuni sono, 1. Per mostrare toast e notifiche quando l'utente è in BG. 2.Per eseguire alcune attività per la prima volta l'utente proviene da BG, come un sondaggio, ridisegna ecc.

La soluzione di Idolon e altri si occupa della prima parte, ma non della seconda. Se ci sono più attività nella tua app e l'utente passa da una all'altra, quando sarai nella seconda attività, il flag visibile sarà falso. Quindi non può essere usato in modo deterministico.

Ho fatto qualcosa che mi è stato suggerito da CommonsWare: "Se il Servizio determina che non ci sono attività visibili e rimane tale per un certo periodo di tempo , interrompere il trasferimento dei dati al successivo punto logico di arresto".

La linea in grassetto è importante e può essere utilizzata per ottenere il secondo elemento. Quindi quello che faccio è quando ottengo onActivityPaused (), non cambiare direttamente il visibile in falso, invece ho un timer di 3 secondi (che è il massimo che dovrebbe essere avviata la prossima attività), e se non c'è onActivityResumed ( ) chiama nei prossimi 3 secondi, cambia visibile in falso. Allo stesso modo in onActivityResumed () se c'è un timer, allora lo annullo. Per riassumere, il visibile diventa isAppInBackground.

Spiacenti, impossibile copiare e incollare il codice ...


-3

Vorrei raccomandarti di usare un altro modo per farlo.

Suppongo che tu voglia mostrare la schermata di avvio mentre il programma è in esecuzione, se è già in esecuzione nel backend, non mostrarlo.

L'applicazione può scrivere continuamente l'ora corrente su un file specifico. Mentre l'applicazione si avvia, controlla l'ultimo timestamp, se current_time-last_time> l'intervallo di tempo specificato per la scrittura dell'ultima ora, significa che l'applicazione è stata arrestata, uccisa dal sistema o dall'utente stesso.

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.