Per sfondo, intendo che nessuna delle attività dell'applicazione è attualmente visibile all'utente?
Per sfondo, intendo che nessuna delle attività dell'applicazione è attualmente visibile all'utente?
Risposte:
Esistono pochi modi per rilevare se l'applicazione è in esecuzione in background, ma solo uno di questi è completamente affidabile:
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.
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 diRunningAppProcessInforecord. Per determinare se l'applicazione si trova nelRunningAppProcessInfo.importancecampo di controllo in primo piano per l'uguaglianza aRunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhileRunningAppProcessInfo.processNameè uguale al nome del pacchetto dell'applicazione.Inoltre, se chiami
ActivityManager.getRunningAppProcesses()dal thread dell'interfaccia utente dell'applicazione, tornerà importanteIMPORTANCE_FOREGROUNDper l'attività, indipendentemente dal fatto che sia effettivamente in primo piano o meno. Chiamalo nel thread in background (ad esempio viaAsyncTask) 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.
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.
OnStoprichiesta a isActivityVisible.
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.
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() .
onStop()seguito super.onStop(). Non controllare lo sfondo in onPause().
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
companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }quindi puoi ottenere lo stato di primo piano conArchLifecycleApp.isForeground()
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?
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);
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;
JobServiceper rilevare che il servizio è in esecuzione in background.
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
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.
onPause, onStopné l' onResumeevento si chiama. Quindi cosa fai se nessuno di questi eventi viene generato ?!
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 ActivityBonStart()verrà chiamato prima di ActivityAonStop().
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:
stateCounter = 0 : Il numero di arresti è uguale al numero di attività avviate, il che significa che l'applicazione è in esecuzione in background.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().
if(Utilities.isApplicationOnBackground()) …a Utilities. Perché altrimenti solo un'attività specifica reagirà all'evento.
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.
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.
MyActivityClasseredita Activitye implementa i metodi del ciclo di vita e che eredita tutte le attività MyActivityClass. Questo non funzionerà PreferenceActivityo MapActivitypenserà (vedi questa domanda )
È 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
}
}
}
Basandosi sulla risposta di @Cornstalks per includere un paio di utili funzioni.
Proprietà aggiuntive:
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;
}
}
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.
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) { }
}
L'unica soluzione corretta:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
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) {
...
}
}
}
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?
Applicationnon ha onPause()o onResume().
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.
Dal momento che non è già stato menzionato, suggerirò ai lettori di esplorare ProcessLifecycleOwner disponibile attraverso i componenti di architettura Android
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:
Se nessuna di queste condizioni è vera, l'app viene considerata in background.
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();
}
}
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++;
}
}
È 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.
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;
}
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.
onStop; tra onPauseed onStopè visibile , ma non in primo piano .
onStop()chiamata, che è in linea con ciò che hai scritto.
onPauseviene chiamato: una modifica recente ti ha corretto.
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:
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 ilActivity. Come posso verificare seActivityè 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.
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
}
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
}
}
}
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 ...
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 ...
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.