J'ai un écran de démarrage sur une minuterie. Mon problème est qu'avant I finish()
mon activité, je dois vérifier que la prochaine activité a démarré car une boîte de dialogue système s'affiche et je ne souhaite que finish()
; une fois que l'utilisateur a sélectionné une option dans la boîte de dialogue?
Je sais qu'il y a beaucoup de questions sur la façon de voir si votre activité est au premier plan, mais je ne sais pas si cela permet également de créer des boîtes de dialogue.
Voici le problème, le rouge est mon activité qui est à l'arrière-plan alors que le dialogue est au premier plan:
EDIT: J'ai simplement essayé de ne pas utiliser finish()
mais mon activité peut alors être replacée dans la pile d'applications que j'essaie d'éviter.
Voici ce qui est recommandé en tant que solution right:
La bonne solution (les crédits vont à Dan, CommonsWare et NeTeInStEiN) Suivre la visibilité de votre application par vous-même en utilisant Méthodes Activity.onPause, Activity.onResume. Stocker le statut "visibilité" dans une autre classe. Les bons choix sont votre propre implémentation du Application ou service (il existe également quelques variantes de cette solution Si vous souhaitez vérifier la visibilité de l'activité depuis le service).
Exemple Implémentez une classe d'application personnalisée (notez la méthode statique isActivityVisible ()):
public class MyApplication extends Application {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
Enregistrez votre classe d'application dans AndroidManifest.xml:
<application
Android:name="your.app.package.MyApplication"
Android:icon="@drawable/icon"
Android:label="@string/app_name" >
Ajoutez onPause et onResume à chaque activité du projet (vous pouvez Créer un ancêtre commun pour vos activités si vous le souhaitez, mais si Votre activité est déjà étendue depuis MapActivity/ListActivity, etc. .. encore besoin d'écrire à la main ce qui suit):
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
Dans votre méthode finish()
, vous souhaitez utiliser isActivityVisible()
pour vérifier si l'activité est visible ou non. Là, vous pouvez également vérifier si l'utilisateur a sélectionné une option ou non. Continuer lorsque les deux conditions sont remplies.
La source mentionne également deux mauvaises solutions ... alors évitez de le faire.
Source: stackoverflow
Si vous ciblez une API de niveau 14 ou supérieur, vous pouvez utiliser 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
....
}
C'est exactement la différence entre les événements onPause
et onStop
de l'activité décrite dans la documentation de la classe Activity .
Si je vous ai bien compris, vous souhaitez appeler finish()
de votre activité onStop
pour la terminer . Voir l'image ci-jointe de l'application de démonstration Activity Lifecycle . Voici comment cela se passe lorsque l'activité B est lancée à partir de l'activité A . L'ordre des événements va du bas vers le haut, ce qui vous permet de voir que l'activité A onStop
est appelée après que l'activité B onResume
a déjà été appelée.
Si une boîte de dialogue est affichée, votre activité est grisée en arrière-plan et seule la variable onPause
est appelée.
Activity :: hasWindowFocus () vous renvoie le booléen dont vous avez besoin.
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;
}
}
Voici un exemple de classe pour vérifier la visibilité de vos activités où que vous soyez.
N'oubliez pas que si vous affichez un dialogue, le résultat sera faux car la boîte de dialogue aura le focus principal. En dehors de cela, c'est vraiment pratique et plus fiable que les solutions suggérées.
Utilisez un Application qui implémente ActivityLifecycleCallbacks et utilisez-le pour suivre les événements de cycle de vie Activités dans votre application. Notez que ActivityLifecycleCallbacks sont pour les api Android> = 14. Pour les api Android précédents, vous devrez l'implémenter vous-même dans toutes vos activités ;-)
Utilisez Application lorsque vous devez partager/stocker des états à travers des activités.
Vous pouvez vérifier l'état d'un processus en cours d'exécution avec cette classe RunningAppProcessInfo
Récupérez la liste de processus en cours d'exécution avec ActivityManager.getRunningAppProcesses () Et filtrez la liste des résultats pour rechercher le RunningAppProcessInfo souhaité et vérifier son "importance".
J'ai créer un projet sur github app-foreground-background-listen
qui utilise une logique très simple et fonctionne bien avec tous les niveaux d'API Android.
Utilisez l'intervalle de temps entre la pause et la reprise depuis l'arrière-plan pour déterminer s'il est réveillé de l'arrière-plan
En application personnalisée
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");
}
Dans la classe BaseActivity
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
Je pense avoir une meilleure solution. Parce que vous pouvez construire simplement MyApplication.activityResumed (); à chaque activité par une extension.
Tout d'abord, vous devez créer (comme 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;
}
Ensuite, vous devez ajouter la classe d'application à AndroidManifest.xml
<application
Android:name="your.app.package.MyApplication"
Android:icon="@drawable/icon"
Android:label="@string/app_name" >
Ensuite, créez la classe ActivityBase
public class ActivityBase extends Activity {
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
}
Enfin, lorsque vous créez une nouvelle activité, vous pouvez simplement l'étendre par ActivityBase au lieu de Activity.
public class Main extends ActivityBase {
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
}
Pour moi, c'est une meilleure méthode car vous devez simplement vous rappeler d'étendre par ActivityBase. De plus, vous pourrez élargir votre fonction de base à l'avenir. Dans mon cas, j'ai ajouté des destinataires pour mon service et des alertes sur le réseau dans une classe.
Si vous voulez vérifier la visibilité de votre application, vous pouvez simplement appeler
MyApplication.isActivityVisible()
En 2019, avec l'aide de la nouvelle bibliothèque de support 28+
ou AndroidX, vous pouvez simplement utiliser:
val isActivityInForeground = activity.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)
Vous pouvez en lire plus dans la documentation pour comprendre ce qui s'est passé sous le capot.
Ceci peut être réalisé efficacement en utilisant Application.ActivityLifecycleCallbacks } _
Par exemple, prenons le nom de la classe d'activité comme ProfileActivity permet de savoir s'il est au premier plan ou à l'arrière-plan
nous devons d’abord créer notre classe d’application en étendant classe d’application
qui met en œuvre
Application.ActivityLifecycleCallbacks
Soit ma classe d'application comme suit
Classe d'application
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;
}
}
dans la classe ci-dessus, il y a un remplacement méthord onActivityResumed 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;
}
où toutes les instances d’activités actuellement affichées à l’écran peuvent être trouvées, il suffit de vérifier si votre activité est à l’écran ou non selon la méthode ci-dessus.
Enregistrez votre classe d'application dans le fichier manifest.xml
<application
Android:name=".AppController" />
Pour vérifier la météo Activité en avant-plan ou en arrière-plan selon la solution ci-dessus, appelez la méthode suivante aux endroits que vous devez vérifier
AppController applicationControl = (AppController) getApplicationContext();
if(applicationControl.isActivityInForeground()){
Log.d("TAG","Activity is in foreground")
}
else
{
Log.d("TAG","Activity is in background")
}
Je dois dire que votre flux de travail n’est pas conforme à la norme Android. Sous Android, vous n'avez pas besoin de finish()
votre activité si vous souhaitez ouvrir une autre activité à partir de Intent. Pour la commodité de l'utilisateur, Android permet à l'utilisateur d'utiliser la touche «Retour» pour revenir de l'activité que vous avez ouverte à votre application.
Il suffit donc de laisser le système vous arrêter et de sauvegarder tout ce dont vous avez besoin lorsque votre activité est rappelée.
avez-vous essayé de ne pas appeler finish et de mettre "Android: noHistory =" true "dans le manifeste? Ceci empêchera l'activité de passer à la pile.
Si vous voulez savoir si une activité de votre application est visible à l'écran, vous pouvez procéder de la manière suivante:
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) {}}
Créez simplement un singleton de cette classe et définissez-le dans votre instance d'application comme ci-dessous:
class App extends Application{
@Override
public void onCreate() {
registerActivityLifecycleCallbacks(myAppActivityCallbacks);
}
}
Ensuite, vous pouvez utiliser la méthode isAnyActivityVisible () de votre instance MyAppActivityCallbacks partout!
Enregistrez un drapeau si vous êtes suspendu ou repris. Si vous êtes repris, cela signifie que vous êtes au premier plan
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();
}
}
Si vous utilisez finish()
uniquement pour éviter qu'une nouvelle application ne démarre dans la pile (tâche) de votre application, vous pouvez utiliser l'indicateur Intent.FLAG_ACTIVITY_NEW_TASK
lors du démarrage d'une nouvelle application et ne pas appeler du tout finish()
. Selon documentation , il s'agit de l'indicateur à utiliser pour implémenter un comportement de style "launcher".
// just add this line before you start an activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Est-ce que Activity.onWindowFocusChanged(boolean hasFocus)
serait utile ici? Cela, ajouté à un indicateur de niveau de classe, quelque chose comme isFocused
qui onWindowFocusChanged
définit, serait un moyen facile de savoir à tout moment de votre activité si elle est ciblée ou non. À la lecture de la documentation, il semblerait que la valeur "false" soit correctement définie dans toutes les situations où l'activité n'est pas directement au "premier plan" physique, comme si une boîte de dialogue était affichée ou que la barre de notification était fermée.
Exemple:
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
}
}
Pourquoi ne pas utiliser les émissions pour cela? la deuxième activité (celle qui doit être active) peut envoyer une émission locale comme celle-ci:
//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);
puis écrivez un récepteur simple dans l'activité 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!
}
};
et enregistrez votre nouveau récepteur auprès de LocalBroadcastManager pour écouter l'émission de votre deuxième activité:
//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"));
REMARQUE: vous pouvez utiliser une ressource constante ou une chaîne pour la chaîne "identificateur de diffusion".
Une solution possible pourrait être de définir un indicateur lors de l'affichage de la boîte de dialogue système, puis dans la méthode onStop du cycle de vie de l'activité, recherchez l'indicateur. S'il est vrai, terminez l'activité.
Par exemple, si la boîte de dialogue système est déclenchée par un clic sur un bouton, l’écouteur onclick peut ressembler à
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
}
};
et en arrêt d'activité:
@Override
protected void onStop() {
if(checkFlag){
finish();
}
super.onStop();
}
Voici une solution utilisant la classe 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;
}
}
Vous pouvez simplement l'utiliser comme suit,
((AppSingleton)context.getApplicationContext()).isOnForeground(context_activity);
Si vous avez une référence à l'activité requise ou utilisez le nom canonique de l'activité, vous pouvez savoir si elle est au premier plan ou non. Cette solution peut ne pas être infaillible. Par conséquent, vos commentaires sont vraiment les bienvenus.
Je ne sais pas pourquoi personne n'a parlé de SharedPreferences, pour l'activité A, définir une SharedPreference comme celle-ci (par exemple dans onPause ()):
SharedPreferences pref = context.getSharedPreferences(SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("is_activity_paused_a", true);
editor.commit();
Je pense que c'est le moyen fiable de suivre la visibilité des activités.