android - programacion - ¿Cómo verificar si la actividad está en primer plano o en un fondo visible?
manual de android en pdf (21)
Dos posibles soluciones:
1) Actividad CallCycle Callbacks
Use una Application que implemente android.app.Application.ActivityLifecycleCallbacks y úsela para rastrear eventos de ciclo de vida de actividades en su aplicación. Tenga en cuenta que ActivityLifecycleCallbacks es para api de Android> = 14. Para la API previa de Android, deberá implementarla usted mismo en todas sus actividades ;-)
Use la Application cuando necesite compartir / almacenar estados en diferentes actividades.
2) Verifica la ejecución de las informaciones del proceso
Puede verificar el estado de un proceso en ejecución con esta clase RunningAppProcessInfo
Obtenga la lista de procesos en ejecución con ActivityManager.getRunningAppProcesses() y filtre la lista de resultados para verificar el RunningAppProcessInfo deseado y verifique su "importancia"
Tengo una pantalla de bienvenida en un temporizador. Mi problema es que antes de finish()
mi actividad necesito verificar que la siguiente actividad haya comenzado porque aparece un cuadro de diálogo del sistema y solo quiero finish()
; una vez que el usuario ha seleccionado una opción del cuadro de diálogo?
Sé que hay muchas preguntas sobre cómo ver si tu actividad está en primer plano, pero no sé si esto permite cuadros de diálogo en la parte superior de la actividad también.
Aquí está el problema, el rojo es mi actividad que está en el fondo mientras el diálogo está en primer plano:
EDITAR: Intenté simplemente no usar finish()
pero luego mi actividad puede volver a la pila de aplicaciones que trato de evitar.
¿Intentó no llamar al final y poner "android: noHistory =" true "en el manifiesto? Esto evitará que la actividad vaya a la pila.
¿Por qué no usar transmisiones para esto? la segunda actividad (la que debe estar activa) puede enviar una transmisión local como esta:
//put this in onCreate(..) or any other lifecycle method that suits you best
//notice the string sent to the intent, it will be used to register a receiver!
Intent result = new Intent("broadcast identifier");
result.putString("some message");//this is optional
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(result);
luego escribe un receptor simple dentro de la actividad splash:
//this goes on the class level (like a class/instance variable, not in a method) of your splash activity:
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//kill activity here!!!
//mission accomplished!
}
};
y registre su nuevo receptor con LocalBroadcastManager para escuchar la transmisión de su segunda actividad:
//notice the string sent to the intent filter, this is where you tell the BroadcastManager which broadcasts you want to listen to!
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(receiver, new IntentFilter("broadcast identifier"));
TEN EN CUENTA que puedes usar una constante o un recurso de cadena para la cadena "identificador de difusión".
¿Sería útil Activity.onWindowFocusChanged(boolean hasFocus)
aquí? Eso, más un indicador de nivel de clase, algo así como que se onWindowFocusChanged
conjuntos onWindowFocusChanged
, sería una manera fácil de contar en cualquier punto de tu actividad si está enfocado o no. Después de leer los documentos, parece que establecerá correctamente "falso" en cualquier situación donde la actividad no esté directamente en el "primer plano" físico, como si se muestra un cuadro de diálogo o se tira hacia abajo la bandeja de notificaciones.
Ejemplo:
boolean isFocused;
@Override
void onWindowFocusChanged (boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
isFocused = hasFocus;
}
void someMethod() {
if (isFocused) {
// The activity is the foremost object on the screen
} else {
// The activity is obscured or otherwise not visible
}
}
Aquí hay una solución que usa la clase de Application
.
public class AppSingleton extends Application implements Application.ActivityLifecycleCallbacks {
private WeakReference<Context> foregroundActivity;
@Override
public void onActivityResumed(Activity activity) {
foregroundActivity=new WeakReference<Context>(activity);
}
@Override
public void onActivityPaused(Activity activity) {
String class_name_activity=activity.getClass().getCanonicalName();
if (foregroundActivity != null &&
foregroundActivity.get().getClass().getCanonicalName().equals(class_name_activity)) {
foregroundActivity = null;
}
}
//............................
public boolean isOnForeground(@NonNull Context activity_cntxt) {
return isOnForeground(activity_cntxt.getClass().getCanonicalName());
}
public boolean isOnForeground(@NonNull String activity_canonical_name) {
if (foregroundActivity != null && foregroundActivity.get() != null) {
return foregroundActivity.get().getClass().getCanonicalName().equals(activity_canonical_name);
}
return false;
}
}
Simplemente puede usarlo de la siguiente manera
((AppSingleton)context.getApplicationContext()).isOnForeground(context_activity);
Si tiene una referencia a la Actividad requerida o usa el nombre canónico de la Actividad, puede averiguar si está en primer plano o no. Esta solución puede no ser infalible. Por lo tanto, sus comentarios son realmente bienvenidos.
Creo que tengo una mejor solución. Porque puedes compilar simplemente MyApplication.activityResumed (); a cada actividad por una extensión.
En primer lugar, debe crear (como CyberneticTwerkGuruOrc)
public class MyApplication extends Application {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
A continuación, debe agregar la clase de aplicación a AndroidManifest.xml
<application
android:name="your.app.package.MyApplication"
android:icon="@drawable/icon"
android:label="@string/app_name" >
Luego, crea la clase ActivityBase
public class ActivityBase extends Activity {
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
}
Finalmente, cuando crea una nueva actividad, puede simplemente ampliarla por ActivityBase en lugar de Activity.
public class Main extends ActivityBase {
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
}
Para mí es un método mejor porque tienes que recordar acerca de extender por ActivityBase. Además, puedes expandir tu función base en el futuro. En mi caso, agregué receptores para mi servicio y alertas sobre la red en una clase.
Si quiere verificar la visibilidad de su aplicación, simplemente puede llamar
MyApplication.isActivityVisible()
Debo decir que su flujo de trabajo no está en una forma estándar de Android. En Android, no necesita finish()
su actividad si desea abrir otra actividad desde Intent. En cuanto a la conveniencia del usuario, Android le permite al usuario usar la tecla ''atrás'' para retroceder desde la actividad que abrió en su aplicación.
Deje que el sistema detenga su actividad y guarde todo lo que necesite cuando su actividad sea devuelta.
Esa es exactamente la diferencia entre los eventos onPause
y onStop
de la actividad como se describe en la documentación de la clase Activity .
Si lo entiendo correctamente, lo que quiere hacer es llamar a finish()
desde su actividad en onStop
para terminarlo. Vea la imagen adjunta de la aplicación Activity Lifecycle Demo . Así es como se ve cuando la Actividad B se lanza desde la Actividad A. El orden de los eventos es de abajo hacia arriba para que pueda ver que se llama a Actividad A onStop
después de que ya se haya llamado a la Actividad B en onResume
.
En caso de que se muestre un cuadro de diálogo, su actividad se atenúa en segundo plano y solo se llama a onPause
.
Esto es lo que se recomienda como la solución correcta :
La solución correcta (los créditos van a Dan, CommonsWare y NeTeInStEiN) Haga un seguimiento de la visibilidad de su aplicación usted mismo usando los métodos Activity.onPause, Activity.onResume. Almacenar el estado de "visibilidad" en alguna otra clase. Las buenas elecciones son su propia implementación de la Aplicación o un Servicio (también hay algunas variaciones de esta solución si desea verificar la visibilidad de la actividad desde el servicio).
Ejemplo Implementar clase de aplicación personalizada (tenga en cuenta el método isActivityVisible () static):
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;
}
Registre su clase de aplicación en AndroidManifest.xml:
<application
android:name="your.app.package.MyApplication"
android:icon="@drawable/icon"
android:label="@string/app_name" >
Agregue OnPause y OnResume a cada actividad en el proyecto (puede crear un ancestro común para sus actividades si lo desea, pero si su actividad ya está extendida desde MapActivity / ListActivity, debe escribir lo siguiente a mano) :
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
En su método finish()
, desea usar isActivityVisible()
para verificar si la actividad es visible o no. Allí también puede verificar si el usuario ha seleccionado una opción o no. Continúa cuando se cumplan ambas condiciones.
La fuente también menciona dos soluciones incorrectas ... así que evita hacer eso.
Fuente:
Esto puede lograr esto de una manera eficiente mediante el uso de android.app.Application.ActivityLifecycleCallbacks
Por ejemplo, tomemos el nombre de clase de actividad, ya que ProfileActivity permite determinar si está en primer plano o en segundo plano.
primero tenemos que crear nuestra clase de Application extendiendo la Application
que implementa
Application.ActivityLifecycleCallbacks
Permite ser mi clase de aplicación de la siguiente manera
Clase de aplicación
public class AppController extends Application implements Application.ActivityLifecycleCallbacks {
private boolean activityInForeground;
@Override
public void onCreate() {
super.onCreate();
//register ActivityLifecycleCallbacks
registerActivityLifecycleCallbacks(this);
}
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
//Here you can add all Activity class you need to check whether its on screen or not
activityInForeground = activity instanceof ProfileActivity;
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
public boolean isActivityInForeground() {
return activityInForeground;
}
}
en la clase anterior hay un método de anulación en la ActividadResultado de ActivityLifecycleCallbacks
@Override
public void onActivityResumed(Activity activity) {
//Here you can add all Activity class you need to check whether its on screen or not
activityInForeground = activity instanceof ProfileActivity;
}
donde se puede encontrar toda la instancia de actividad que se muestra actualmente en la pantalla, simplemente verifique si Su actividad está en pantalla o no por el método anterior.
Registre su clase de aplicación en manifest.xml
<application
android:name=".AppController" />
Para verificar si la actividad meteorológica está en primer plano o en segundo plano según la solución anterior, llame al siguiente método en los lugares que necesite verificar
AppController applicationControl = (AppController) getApplicationContext();
if(applicationControl.isActivityInForeground()){
Log.d("TAG","Activity is in foreground")
}
else
{
Log.d("TAG","Activity is in background")
}
Guarde una bandera si está en pausa o reanudada. Si se reanuda significa que estás en primer plano
boolean isResumed = false;
@Override
public void onPause() {
super.onPause();
isResumed = false;
}
@Override
public void onResume() {
super.onResume();
isResumed = true;
}
private void finishIfForeground() {
if (isResumed) {
finish();
}
}
No sé por qué nadie habló sobre las Preferencias compartidas, para la Actividad A, estableciendo una SharedPreference así (por ejemplo en onPause ()):
SharedPreferences pref = context.getSharedPreferences(SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("is_activity_paused_a", true);
editor.commit();
Creo que esta es la manera confiable de rastrear la visibilidad de las actividades.
Si desea saber si alguna actividad de su aplicación es visible en la pantalla, puede hacer algo como esto:
public class MyAppActivityCallbacks implements Application.ActivityLifecycleCallbacks {
private Set<Class<Activity>> visibleActivities = new HashSet<>();
@Override
public void onActivityResumed(Activity activity) {
visibleActivities.add((Class<Activity>) activity.getClass());
}
@Override
public void onActivityStopped(Activity activity) {
visibleActivities.remove(activity.getClass());
}
public boolean isAnyActivityVisible() {
return !visibleActivities.isEmpty();
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
@Override
public void onActivityStarted(Activity activity) {}
@Override
public void onActivityPaused(Activity activity) {}
@Override
public void onActivityDestroyed(Activity activity) {}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}}
Solo crea un singleton de esta clase y configúralo en tu instancia de aplicación como se muestra a continuación:
class App extends Application{
@Override
public void onCreate() {
registerActivityLifecycleCallbacks(myAppActivityCallbacks);
}
}
¡Entonces puede usar el método isAnyActivityVisible () de su instancia de MyAppActivityCallbacks en cualquier lugar!
Si se dirige al nivel de API 14 o superior, se puede usar android.app.Application.ActivityLifecycleCallbacks
public class MyApplication extends Application implements ActivityLifecycleCallbacks {
private static boolean isInterestingActivityVisible;
@Override
public void onCreate() {
super.onCreate();
// Register to be notified of activity state changes
registerActivityLifecycleCallbacks(this);
....
}
public boolean isInterestingActivityVisible() {
return isInterestingActivityVisible;
}
@Override
public void onActivityResumed(Activity activity) {
if (activity instanceof MyInterestingActivity) {
isInterestingActivityVisible = true;
}
}
@Override
public void onActivityStopped(Activity activity) {
if (activity instanceof MyInterestingActivity) {
isInterestingActivityVisible = false;
}
}
// Other state change callback stubs
....
}
Si usa finish()
solo para evitar que la nueva aplicación comience en la pila (tarea) de su aplicación, puede usar el indicador Intent.FLAG_ACTIVITY_NEW_TASK
, cuando comience una nueva aplicación y no llame a finish()
en absoluto. De acuerdo con la documentation , esta es la bandera que se utilizará para implementar un comportamiento de estilo "iniciador".
// just add this line before you start an activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Solía hacer,
si la actividad no está en primer plano
getIntent ()
devolverá nulo. : = P
Tengo un proyecto de creación en github app-foreground-background-listen
que usa una lógica muy simple y funciona bien con todos los niveles API de Android.
Una posible solución podría ser establecer un indicador mientras se muestra el diálogo del sistema y luego, en el método onStop del ciclo de vida de la actividad, verificar que el indicador, si es verdadero, termine la actividad.
Por ejemplo, si el cuadro de diálogo del sistema se activa mediante un clic, entonces el oyente onclick podría ser como
private OnClickListener btnClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
CheckActivity.this.startActivity(Intent.createChooser(intent, "Complete action using"));
checkFlag = true; //flag used to check
}
};
y en la parte superior de la actividad:
@Override
protected void onStop() {
if(checkFlag){
finish();
}
super.onStop();
}
Usa el intervalo de tiempo entre pausa y currículum desde el fondo para determinar si está despierto desde el fondo
En aplicación personalizada
private static boolean isInBackground;
private static boolean isAwakeFromBackground;
private static final int backgroundAllowance = 10000;
public static void activityPaused() {
isInBackground = true;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isInBackground) {
isAwakeFromBackground = true;
}
}
}, backgroundAllowance);
Log.v("activity status", "activityPaused");
}
public static void activityResumed() {
isInBackground = false;
if(isAwakeFromBackground){
// do something when awake from background
Log.v("activity status", "isAwakeFromBackground");
}
isAwakeFromBackground = false;
Log.v("activity status", "activityResumed");
}
En la clase BaseActivity
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
Usa estos métodos dentro de una Activity
.
isDestroyed()
Agregado en Api 17
Devuelve verdadero si la llamada final onDestroy () se realizó en la Actividad, por lo que esta instancia ahora está muerta.
isFinishing()
Agregado en Api 1
Compruebe si esta actividad está en proceso de finalización, ya sea porque ha llamado a finish () o porque alguien más ha solicitado que termine. Esto se usa a menudo en onPause () para determinar si la actividad simplemente está pausando o terminando por completo.
Un error común con AsyncTask
es capturar una referencia fuerte a la Activity
del host (o Fragment
):
class MyActivity extends Activity {
private AsyncTask<Void, Void, Void> myTask = new AsyncTask<Void, Void, Void>() {
// Don''t do this! Inner classes implicitly keep a pointer to their
// parent, which in this case is the Activity!
}
}
Esto es un problema porque AsyncTask
puede sobrevivir fácilmente a la Activity
principal, por ejemplo, si ocurre un cambio de configuración mientras se está ejecutando la tarea.
La forma correcta de hacer esto es hacer que su tarea sea una clase static
, que no capture el elemento principal, y manteniendo una referencia débil a la Activity
:
class MyActivity extends Activity {
static class MyTask extends AsyncTask<Void, Void, Void> {
// Weak references will still allow the Activity to be garbage-collected
private final WeakReference<MyActivity> weakActivity;
MyTask(MyActivity myActivity) {
this.weakActivity = new WeakReference<>(myActivity);
}
@Override
public Void doInBackground(Void... params) {
// do async stuff here
}
@Override
public void onPostExecute(Void result) {
// Re-acquire a strong reference to the activity, and verify
// that it still exists and is active.
MyActivity activity = weakActivity.get();
if (activity == null
|| activity.isFinishing()
|| activity.isDestroyed()) {
// activity is no longer valid, don''t do anything!
return;
}
// The activity is still valid, do main-thread stuff here
}
}
}
Activity::hasWindowFocus() devuelve el booleano que necesita.
public class ActivityForegroundChecker extends TimerTask
{
private static final long FOREGROUND_CHECK_PERIOD = 5000;
private static final long FIRST_DELAY = 3000;
private Activity m_activity;
private Timer m_timer;
public ActivityForegroundChecker (Activity p_activity)
{
m_activity = p_activity;
}
@Override
public void run()
{
if (m_activity.hasWindowFocus() == true) {
// Activity is on foreground
return;
}
// Activity is on background.
}
public void start ()
{
if (m_timer != null) {
return;
}
m_timer = new Timer();
m_timer.schedule(this, FIRST_DELAY, FOREGROUND_CHECK_PERIOD);
}
public void stop ()
{
if (m_timer == null) {
return;
}
m_timer.cancel();
m_timer.purge();
m_timer = null;
}
}
Aquí hay una clase de ejemplo para verificar la visibilidad de sus actividades desde donde se encuentre.
Recuerde que si muestra un cuadro de diálogo , el resultado será falso ya que el cuadro de diálogo tendrá el foco principal. Aparte de eso, es realmente útil y más confiable que las soluciones sugeridas.