web-dev-qa-db-fra.com

Pourquoi BroadcastReceiver fonctionne même lorsque l'application est en arrière-plan?

Je vérifie la connectivité Internet dans mon application à l'aide de BroadcastReceiver et affiche une boîte de dialogue d'alerte en cas de perte de la connexion. Ça fonctionne bien. Mais mon problème est que BroadcastReceiver fonctionne même si mon application est en arrière-plan. La boîte de dialogue s'ouvre lorsque la connexion Internet est perdue, même si l'utilisateur est dans une autre application. Ceci ruine totalement mon application. 

Quelqu'un a-t-il une idée de la manière de limiter le récepteur de radiodiffusion dans l'application uniquement? 

Voici mon BroadcastReceiver: 

 public class ConnectivityChangedReceiver extends BroadcastReceiver{

    @Override
    public void onReceive( Context context, Intent intent )
     {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE );
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
    } else {
        try{
            Intent i=new Intent(context, InternetDialogActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        } catch(Exception e){
            e.printStackTrace();
        }
    }
  }
}

Et l'activité est:

 public class InternetDialogActivity extends Activity implements OnClickListener{
   @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.internet_dialog_box);
      getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
      Button retryButton = (Button) findViewById(R.id.retryInternetButton);
      retryButton.setOnClickListener(this);
    }

   public boolean checkConnectivity(){
       ConnectivityManager connectivityManager   = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
           NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

       if (networkInfo!=null && networkInfo.isConnected()) {
           finish();
           return true;
       } else {
           Intent intent = getIntent();
           finish();
           startActivity(intent);
           return false; 
      }
   }

   @Override
   public void onClick(View view) {
      switch(view.getId()){
         case R.id.retryInternetButton:
             try{
                 checkConnectivity();
             } catch(Exception e){
                 e.printStackTrace();
             }
             break;
       }
   }
   }

Voici comment j'ai déclaré récepteur et activité dans le manifeste:

  <receiver Android:name="com.lisnx.service.ConnectivityChangedReceiver"
        Android:label="NetworkConnection">
        <intent-filter>
            <action Android:name="Android.net.conn.CONNECTIVITY_CHANGE"/>
        </intent-filter>
  </receiver>

  <activity Android:name="com.lisnx.activity.InternetDialogActivity"
        Android:configChanges="orientation|keyboardHidden"
        Android:theme="@Android:style/Theme.Dialog" />

J'ai lu que nous pouvons restreindre BroadcastReceiver à fonctionner au sein de l'application en ne le déclarant pas dans le manifeste. Mais je ne sais pas comment le récepteur fonctionnera alors? Aidez-moi, s'il vous plaît. Je suis vraiment coincé dessus. Merci d'avance.

18
Yogesh Somani

A BroadcastReceiver fonctionne lorsque l'application est en arrière-plan car les événements que le destinataire capte sont envoyés de manière globale et chaque application est enregistrée pour y être écoutée, qu'elle soit ou non en cours d'exécution.

Pour résoudre ce problème, dans le code BroadcastReceiver de votre onReceive, vérifiez si votre application est au premier plan.

Il existe une méthode, et la seule que je connaisse, qui soit toujours efficace. Vous devez suivre vos actions de pause/reprise pour votre application. Assurez-vous de vérifier cela dans chaque activité.

Il y a quelques exemples de code dans cette réponse (solution n ° 1). Dans votre cas, vous voudriez vérifier MyApplication.isActivityVisible() == true comme une validation avant de faire quoi que ce soit de votre BroadcastReceiver.

37
Eric

Avez-vous essayé de supprimer le filtre d’intention du manifeste et de l’inscrire/le désenregistrer en activité? Vous pouvez donc essayer d’enregistrer le filtre d’intention dans onStart () et de le désenregistrer sur les méthodes onStop (). Le code va quelque chose comme ça:

static final String ACTION = "Android.net.conn.CONNECTIVITY_CHANGE";


IntentFilter filter = new IntentFilter(ACTION);
this.registerReceiver(ConnectivityChangedReceiver, filter);


unregisterReceiver(ConnectivityChangedReceiver);

Vous devriez également en savoir plus sur Cycle de vie d'activité , si ce n'est pas encore familier.

17
soreal

Vous devez enregistrer/désinscrire votre BroadcastReceiver dans onPause() et onResume() de chaque activité. Ensuite, vous savez que le récepteur n’écoute que lorsque votre application est au premier plan. Vous pouvez facilement le faire en créant votre propre BaseActivity qui étend Activity et remplace les fonctions onPause() et onResume() et enregistre/annule l’enregistrement de votre récepteur. Ayez simplement toutes vos activités étendues BaseActivity et appelez-les comme suit pour super.onResume() et super.onPause(). Voici un exemple de code:

public class BaseActivity extends Activity {
    // Create an IntentFilter to listen for connectivity change events
    static IntentFilter filter = new IntentFilter("Android.net.conn.CONNECTIVITY_CHANGE");
    // Create an instance of our BroadcastReceiver
    static ConnectivityChangedReceiver receiver = new ConnectivityChangedReceiver();

    @Override
    protected void onPause() {
        super.onPause();
        // Stop listening for connectivity change events
        unregisterReceiver(receiver);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Listen for connectivity change events
        registerReceiver(receiver, filter);
    }
}

Toutes vos activités devraient être étendues à BaseActivity et s’ils doivent faire quelque chose de spécial dans onResume() ou onPause(), assurez-vous d’appeler super.onXXXXX() comme ceci:

public MyActivity extends BaseActivity {
    @Override
    protected void onResume() {
        super.onResume();
        // Here you do whatever you need to do in onResume() of your activity
        ...
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Here you do whatever you need to do in onPause() of your activity
        ...
    }
}

Je n'ai pas fait passer le code dans un compilateur, donc je m'excuse s'il y a une faute de frappe ou si j'ai oublié quelque chose. 

13
David Wasser

Je pense que vous devrez vous assurer que vous n'utilisez pas le récepteur lorsque l'application est en arrière-plan. Pour cela, vous devrez utiliser toutes les activités des méthodes onPause () et onResume ().

4
Final Destination

Pour créer un Broadcast Receiver qui ne se déclenche que lorsque l'application est en cours d'exécution, suivez le code ci-dessous.

1. Créez votre récepteur de radiodiffusion comme ceci:


import Android.content.BroadcastReceiver;
import Android.content.Context;
import Android.content.Intent;

public class InternetStatusNotifier extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        //Recieve notification here

    }

}

2. Créez une activité ou un fragment pour lequel vous souhaitez que le récepteur de radiodiffusion fonctionne comme ceci:


import Android.app.Activity;
import Android.content.IntentFilter;
import Android.os.Bundle;

public class MainActivity extends Activity {
    private InternetStatusNotifier mInternetStatusNotifier;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mInternetStatusNotifier = new InternetStatusNotifier();
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onResume() {
        registerReceiver(mInternetStatusNotifier, new IntentFilter(
                "Android.net.conn.CONNECTIVITY_CHANGE"));
        super.onResume();
    }

    @Override
    protected void onPause() {
        unregisterReceiver(mInternetStatusNotifier);
        super.onPause();
    }

Remarque: C'est ainsi que vous utilisez le récepteur d'émissions d'une manière spécifique à l'écran. Seules les images affichées recevront les émissions de cette manière. Lorsque vous enregistrez une diffusion à l'aide d'un fichier manifeste, ils sont même reçus lorsque l'application est fermée

3

Autant que je sache, si le récepteur de radiodiffusion est enregistré dans le fichier manifest.xml, le récepteur de radiodiffusion existe tant que l'application existe. De plus, les destinataires enregistrés dynamiquement (c’est-à-dire, enregistrez votre BroadcastReceiver par programme) sont appelés sur le fil de l’UI. Cela signifie que vos destinataires bloquent toute manipulation de l'interface utilisateur et que la méthode onReceive() devrait donc être aussi rapide que possible. 

Cependant, je vais essayer de discuter des informations sur Broadcast Receiver. Mais, il faut d’abord savoir quelques informations. Tout d'abord, Broadcast receiver est un composant d'application autonome, ce qui signifie qu'il continuera à fonctionner même lorsque d'autres composants de l'application ne fonctionnent pas. C'est pourquoi nous annulons l'enregistrement du récepteur de diffusion dans onPause sur l'activité. En outre, le développeur doit enregistrer cela dans la mise en œuvre Activity.onResume()

Deuxièmement, le développeur ne doit pas annuler l’inscription dans Activity.onSaveInstanceState(), car celle-ci ne sera pas appelée si l’utilisateur revient dans la pile d’historique. J'ai mis cette information de Documentation BroadcastReceiver .

Un autre point est qu'un objet BroadcastReceiver n'est valide que pendant la durée de l'appel à onReceive (). Dès que la méthode onReceive () est terminée, votre BroadcastReceiver se termine.

Maintenant, comment enregistrer votre récepteur par programme:

public abstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter)

Ici, BroadcastReceiver-receiver sera appelé lorsque toute intention de diffusion correspond au filtre.
Et IntentFilter- Intent spécifie l'événement que votre récepteur doit écouter.

Registre:

YourConnectionListener receiver;
this.reciever = new YourConnectionListener();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(this.reciever, filter);

Envoyé vos informations de diffusion:

Intent intent = new Intent();
intent.putExtra("Message", "Your connectivity info has Changed!!"); 
this.sendBroadcast(intent);

Receveur:

Maintenant, vous devez recevoir la diffusion. Android appelle la méthode onReceive () sur tous les récepteurs de diffusion enregistrés chaque fois que l'événement se produit. Supposons que vous souhaitiez être averti chaque fois que la connexion est modifiée.

public class YourConnectionListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
         // your Code
    }
}   

onReceive() a deux arguments:

  • context: objet de contexte que vous pouvez utiliser pour accéder à des informations supplémentaires ou pour démarrer des services ou des activités.
  • intention: intention utilisée pour enregistrer votre récepteur. Cet objet contient des informations supplémentaires que vous pouvez utiliser dans votre implémentation .
    En outre, les développeurs doivent éviter toute tâche de longue durée dans votre BroadcastReceiver. Ainsi, dans les récepteurs enregistrés de manière statique et dynamique, Developer doit effectuer des tâches mineures dans le récepteur lui-même. Pour les tâches plus longues, vous devez démarrer un service à partir de votre récepteur. 
3
Ratna Halder

C'est ainsi que fonctionnent les récepteurs de diffusion sous Android. Si vous vous inscrivez pour une diffusion dans le manifeste et que votre application ne s'exécute pas, Android lancera un nouveau processus pour gérer la diffusion. Il est généralement déconseillé d'afficher directement une interface utilisateur à partir d'un récepteur de diffusion, car cela pourrait interrompre d'autres applications. Je ne suis pas non plus convaincu qu'un dialogue universel sur la "connexion perdue" est une bonne idée non plus. Cela devrait probablement être traité par chaque activité utilisant un accès réseau. 

En ce qui concerne la question initiale, vous devez désactiver votre récepteur lorsque votre activité passe en arrière-plan (onPause(), etc.) et l'activer lorsque vous arrivez au premier plan (onResume(), etc.). Mettez enabled=false dans votre manifeste, puis utilisez quelque chose comme ceci dans votre code pour le basculer si nécessaire: 

   public static void toggle(Context context, boolean enable) {
        int flag = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
        ComponentName receiver = new ComponentName(context,
                ConnectivityMonitor.class);

        context.getPackageManager().setComponentEnabledSetting(receiver, flag,
                PackageManager.DONT_KILL_APP);
   }
1
Nikolay Elenkov

Un moyen simple de savoir si l'application est au premier plan ou non 

if((mContext.getPackageName().equalsIgnoreCase(
                ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE))
                        .getRunningTasks(1).get(0).topActivity.getPackageName())))
{
//app is in foreground;
}
0
Naren

Je suggère que vous vérifiiez le réglage Internet de l'application quand quelqu'un l'ouvre, voici le morceau de code suivant lequel je le fais.

public static boolean isNetworkConnected(Context ctx) {  
        ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);  
        NetworkInfo ni = cm.getActiveNetworkInfo();  
        if (ni == null) {  
            return false; // There are no active networks.  
        } else  
            return true;  
    }
0
yubaraj poudel