Comment vérifier si l'activité est au premier plan ou en arrière-plan visible?
j'ai un écran splash sur une minuterie. Mon problème est qu'avant de finish()
mon activité je dois vérifier que l'activité suivante a commencé parce qu'une boîte de dialogue Système apparaît et je veux seulement finish()
; une fois que l'Utilisateur a choisi une option de 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 des boîtes de dialogue au-dessus de l'activité aussi.
voici le problème, le rouge est mon activité qui est à l'arrière-plan tandis que le dialogue est au premier plan:
EDIT: j'ai essayé tout simplement pas à l'aide de finish()
mais alors, mon activité peut être repris dans la pile d'applications dont je suis en train de les éviter.
21 réponses
C'est ce qui est recommandé comme la droite solution:
la bonne solution (les crédits vont à Dan, CommonsWare et NeTeInStEiN) La visibilité du suivi de votre application par vous-même en utilisant Activité.onPause, de l'Activité.méthodes onResume. Conserver le statut "visibilité" dans certains autres de la classe. Les bons choix sont votre propre mise en œuvre de la Application ou un Service (il y a aussi quelques variantes de ce solution si vous souhaitez vérifier la visibilité de l'activité à partir du service).
exemple Implémenter la classe d'Application personnalisée (noter 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 unepause et unesume à chaque activité du projet (vous pouvez créer un ancêtre commun pour votre Activités si vous le souhaitez, mais si votre activité est déjà étendue de MapActivity / ListActivity etc. vous devez encore écrire ce qui suit à la main):
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
dans votre méthode finish()
, vous voulez utiliser isActivityVisible()
pour vérifier si l'activité est visible ou non. 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 erreurs solutions ... alors évitez de faire ça.
Source: stackoverflow
si vous ciblez L'API de niveau 14 ou plus, 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 onPause
et onStop
événements de l'activité comme décrit dans la documentation de classe D'activité .
si je vous comprends bien, ce que vous voulez faire est d'appeler finish()
de votre activité onStop
pour y mettre fin.
Voir l'image ci-jointe de activité Lifecycle Demo App . C'est ce qui se passe lorsque L'activité B est lancée à partir de L'activité A.
Le l'ordre des événements est de bas en haut de sorte que vous pouvez voir que L'activité a onStop
est appelée après que L'activité b onResume
était déjà appelée.
dans le cas où un dialogue est affiché, votre activité est atténuée en arrière-plan et seulement onPause
est appelé.
deux solutions possibles:
1) Callbacks Du Cycle De Vie De L'Activité
utilisez une Application qui implémente ActivityLifecycleCallbacks et utilisez-la pour suivre les événements du cycle de vie des activités dans votre application. Notez que ActivityLifecycleCallbacks sont pour api Android > = 14. Pour l'api Android précédente, vous devrez l'implémenter vous-même dans toutes vos activités; -)
Utilisez Application lorsque vous devez partager / stocker des États à travers les activités.
2) Vérifier les informations du processus d'exécution
Vous pouvez vérifier l'état du processus en cours d'exécution avec cette classe RunningAppProcessInfo
récupération de la liste des processus en cours d'exécution avec ActivityManager.getRunningAppProcesses () et filtrer la liste des résultats pour vérifier RunningAppProcessInfo et vérifier son "importance"
activité::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.
rappelez-vous que si vous montrez un dialogue , le résultat sera faux puisque le dialogue aura la mise au point principale. À part cela, c'est très pratique et plus fiable que les solutions suggérées.
j'ai créer un projet sur github app-au premier plan-arrière-plan-écouter
qui utilise une logique très simple et fonctionne très bien avec tous les niveaux D'API android.
Utiliser l'intervalle de temps entre pause et reprendre à partir du fond de déterminer s'il est éveillé d'arrière-plan
Dans 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");
}
En Classe D'Activité De Base
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
je pense que j'ai une meilleure solution. Parce que vous pouvez construire simplement mon application.activityResumed(); pour 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éer 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, quand vous créez une nouvelle activité, vous pouvez étend simplement par ActivityBase au lieu de L'activité.
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 parce que vous devez juste vous rappeler à propos de extend by ActivityBase. En outre, vous pouvez étendre votre fonction de base à l'avenir. Dans mon cas, j'ai ajouté des récepteurs 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()
avez-vous essayé de ne pas appeler finish, et de mettre"android:noHistory=" true " dans le manifeste? cela permettra d'éviter l'activité d'aller à la pile.
je dois dire que votre flux de travail n'est pas D'une manière Android standard. Dans Android, vous n'avez pas besoin de finish()
votre activité si vous voulez ouvrir une autre activité D'intention. Comme pour la commodité de l'utilisateur, Android permet à l'utilisateur d'utiliser "retour" pour aller à l'arrière de l'activité que vous avez ouvert pour votre application.
alors laissez le système arrêter votre activité et enregistrer tout ce qui est nécessaire lorsque votre activité est rappelée.
Je ne sais pas pourquoi personne n'a parlé de références partagées, pour L'activité A, définir une référence partagée comme cela (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 la façon fiable de suivre les activités visibilty.
Si vous voulez savoir si toute l'activité de votre application est visible sur l'écran, vous pouvez faire quelque chose comme ceci:
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) {}}
il suffit de créer un singleton de cette classe et de le régler dans votre instance D'Application comme ci-dessous:
class App extends Application{
@Override
public void onCreate() {
registerActivityLifecycleCallbacks(myAppActivityCallbacks);
}
}
alors vous pouvez utiliser la méthode isAnyActivityVisible () de votre instance MyAppActivityCallbacks partout!
Enregistrer un drapeau si vous êtes en pause ou reprise. 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();
}
}
une solution possible pourrait être de mettre un drapeau tout en montrant la boîte de dialogue système et puis dans la méthode onStop du cycle de vie de l'activité, vérifier le drapeau, si vrai, terminer l'activité.
par exemple, si la boîte de dialogue du système est déclenchée par un buttonclick, alors l'écouteur onclick pourrait être comme
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 haut d'activité:
@Override
protected void onStop() {
if(checkFlag){
finish();
}
super.onStop();
}
pourquoi ne pas utiliser des émissions pour cela? la deuxième activité (celle qui doit être mise en place) peut envoyer une diffusion 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);
alors é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 avec le gestionnaire local de diffusion 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"));
notez que vous pouvez utiliser une constante ou une ressource de chaîne de caractères pour "la diffusion de l'identificateur de" chaîne de caractères.
si vous utilisez finish()
juste pour éviter la nouvelle application pour commencer dans la pile (tâche) de votre application, vous pouvez utiliser le drapeau Intent.FLAG_ACTIVITY_NEW_TASK
, lors du démarrage de la nouvelle application et ne pas appeler finish()
du tout. Selon la documentation , c'est le drapeau à utiliser pour implémenter un comportement de type "lanceur".
// just add this line before you start an activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
cela peut être réalisé de manière efficace en utilisant L'Application .ActivityLifecycleCallbacks
par exemple, prenons le nom de la classe D'activité comme ProfileActivity permet de savoir si elle est en premier plan ou en arrière-plan
tout d'abord, nous devons créer notre classe d'application en étendant Classe d'Application
qui met en œuvre
de l'Application.ActivityLifecycleCallbacks
permet D'être 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 une commande prioritaire 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é qui sont actuellement affichée à l'écran peut être trouvé, vérifiez si Votre Activité est sur l'Écran ou pas par la méthode ci-dessus.
enregistrez votre classe de demande dans le manifeste.xml
<application
android:name=".AppController" />
pour vérifier que L'activité météorologique est au premier plan ou en arrière-plan, conformément à la solution ci-dessus, appelez la méthode suivante sur les 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")
}
utilisez ces méthodes à l'intérieur d'un Activity
.
isDestroyed()
ajouté dans Api 17
Renvoie true si l'appel final onDestroy() a été fait sur le Activité, donc cette instance est maintenant morte.
isFinishing()
ajouté dans Api 1
Vérifier si cette activité est en cours de finition, soit parce que vous avez appelé finish() sur elle ou quelqu'un d'autre a demandé qu'il a fini. Ceci est souvent utilisé dans onPause() pour déterminer si l'activité est simplement une pause ou la fin complète.
À Partir De Fuites De Mémoire Documentation
une erreur courante avec AsyncTask
est de capturer une référence forte à l'hôte Activity
(ou 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!
}
}
C'est un problème parce que AsyncTask
peut facilement survivre au parent Activity
, par exemple si un changement de configuration se produit pendant que la tâche est en cours d'exécution.
la bonne façon de faire cela est de faire de votre tâche une classe static
, ce qui ne ne pas capturer le parent, et en tenant un faible référence à l'hôte 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
}
}
}
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é requis ou en utilisant le nom canonique de l'Activité, vous pouvez savoir si elle est au premier plan ou non. Cette solution n'est peut-être pas infaillible. Donc vos commentaires sont vraiment les bienvenus.
est-ce que Activity.onWindowFocusChanged(boolean hasFocus)
serait utile ici? Cela, plus un drapeau de classe, quelque chose comme isFocused
que onWindowFocusChanged
ensembles, serait un moyen facile de dire à tout moment dans votre activité si elle est ciblée ou non. En lisant le docs, il semble qu'il serait correctement placé "false" dans toute situation où l'activité n'est pas directement dans le "premier plan" physique, comme si une boîte de dialogue est affichée ou le plateau de notification est tiré vers le bas.
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
}
}
j'avais l'habitude de faire, comme,
si l'activité n'est pas au premier plan
getIntent ()
renverra null. : = P