Comment vérifier si Receiver est enregistré sur Android?

Réponses:

68

Je ne suis pas sûr que l'API fournit directement une API, si vous considérez ce fil :

Je me demandais la même chose.
Dans mon cas, j'ai une BroadcastReceiverimplémentation qui appelle le Context#unregisterReceiver(BroadcastReceiver)passage lui-même comme argument après avoir géré l'intention qu'il reçoit.
Il y a une petite chance que la onReceive(Context, Intent)méthode du récepteur soit appelée plus d'une fois, car elle est enregistrée avec plusieurs IntentFilters, ce qui crée un potentiel de IllegalArgumentExceptionrejet Context#unregisterReceiver(BroadcastReceiver).

Dans mon cas, je peux stocker un membre synchronisé privé à vérifier avant d'appeler Context#unregisterReceiver(BroadcastReceiver), mais ce serait beaucoup plus propre si l'API fournissait une méthode de vérification.

VonC
la source
313

Il n'y a pas de fonction API pour vérifier si un récepteur est enregistré. La solution consiste à mettre votre code dans untry catch block as done below.

try {

 //Register or UnRegister your broadcast receiver here

} catch(IllegalArgumentException e) {

    e.printStackTrace();
}
Daniel Velkov
la source
83
c'est une déception ... :(
Sander Versluys
4
Ce qui est drôle, c'est qu'il ne détecte pas l'erreur pour cet appel à BroadcastReceiver pour registerReceiver (mReceiver, filter1);
JPM
1
@JPM Oui, c'est ça. J'allais stocker une instance de mon récepteur et vérifier pour le désinscrire s'il ne l'est pas null. Mais comme vous l'avez souligné, j'y vais try catch. Ridicule.
9
Downvote to Android pour ne pas avoir créé d'API pour cela. +1 à vous pour avoir fourni une solution de travail :)
Denys Vitali
1
Ce qui est mieux? en utilisant cela ou en utilisant une variable booléenne comme indicateur?
DAVIDBALAS1
34

solution la plus simple

dans le récepteur:

public class MyReceiver extends BroadcastReceiver {   
    public boolean isRegistered;

    /**
    * register receiver
    * @param context - Context
    * @param filter - Intent Filter
    * @return see Context.registerReceiver(BroadcastReceiver,IntentFilter)
    */
    public Intent register(Context context, IntentFilter filter) {
        try {
              // ceph3us note:
              // here I propose to create 
              // a isRegistered(Contex) method 
              // as you can register receiver on different context  
              // so you need to match against the same one :) 
              // example  by storing a list of weak references  
              // see LoadedApk.class - receiver dispatcher 
              // its and ArrayMap there for example 
              return !isRegistered 
                     ? context.registerReceiver(this, filter) 
                     : null;
            } finally {
               isRegistered = true;
            }
    }

    /**
     * unregister received
     * @param context - context
     * @return true if was registered else false
     */
     public boolean unregister(Context context) {
         // additional work match on context before unregister
         // eg store weak ref in register then compare in unregister 
         // if match same instance
         return isRegistered 
                    && unregisterInternal(context);
     }

     private boolean unregisterInternal(Context context) {
         context.unregisterReceiver(this); 
         isRegistered = false;
         return true;
     }

    // rest implementation  here 
    // or make this an abstract class as template :)
    ...
}

dans du code:

MyReceiver myReceiver = new MyReceiver();
myReceiver.register(Context, IntentFilter); // register 
myReceiver.unregister(Context); // unregister 

annonce 1

-- en réponse à:

Ce n'est vraiment pas si élégant car vous devez vous rappeler de définir le drapeau isRegistered après votre inscription. - Rabbin furtif

- méthode "plus ellegant" ajoutée dans le récepteur pour enregistrer et positionner le drapeau

cela ne fonctionnera pas Si vous redémarrez l'appareil ou si votre application a été tuée par le système d'exploitation. - amin Il y a 6 heures

@amin - voir la durée de vie du récepteur enregistré dans le code (non enregistré par le système par l'entrée du manifeste) :)

ceph3us
la source
2
C'est vraiment une solution élégante. FWIW, dans Android Studio, lorsque j'essaie d'étendre BroadcastReceiver, il se plaint et souhaite une substitution de onReceive. Heureusement, dans mon cas, j'avais besoin d'étendre ScreenReceiver, qui fonctionne exactement comme décrit ceph3us ici.
MarkJoel60
Ce n'est vraiment pas si élégant car vous devez vous rappeler de définir le drapeau isRegistered après votre inscription.
Stealth Rabbi
Oui, bien que vous puissiez supprimer cette ligne de votre section "dans le code". myReceiver.isRegistered = true;
Stealth Rabbi
cela ne devrait-il pas être une classe abstraite? l'extension de BroadcastReceiver nécessite que vous implémentiez la méthode onReceive.
York Yang
@YorkYang a ajouté des informations dans la classe en bas
ceph3us
27

J'utilise cette solution

public class ReceiverManager {

    private static List<BroadcastReceiver> receivers = new ArrayList<BroadcastReceiver>();  
    private static ReceiverManager ref;
    private Context context;

    private ReceiverManager(Context context){
        this.context = context;
    }

    public static synchronized ReceiverManager init(Context context) {      
        if (ref == null) ref = new ReceiverManager(context);
        return ref;
    }

    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter intentFilter){
        receivers.add(receiver);
        Intent intent = context.registerReceiver(receiver, intentFilter);
        Log.i(getClass().getSimpleName(), "registered receiver: "+receiver+"  with filter: "+intentFilter);
        Log.i(getClass().getSimpleName(), "receiver Intent: "+intent);
        return intent;
    }

    public boolean isReceiverRegistered(BroadcastReceiver receiver){
        boolean registered = receivers.contains(receiver);
        Log.i(getClass().getSimpleName(), "is receiver "+receiver+" registered? "+registered);
        return registered;
    }

    public void unregisterReceiver(BroadcastReceiver receiver){
        if (isReceiverRegistered(receiver)){
            receivers.remove(receiver);
            context.unregisterReceiver(receiver);
            Log.i(getClass().getSimpleName(), "unregistered receiver: "+receiver);
        }
    }
}
slinden77
la source
2
haha, je les trouve pratiques :) Aperçu plus rapide sur le format et où commence et se termine les choses :) chacun à son goût, je suppose
slinden77
1
mmm, en regardant cela selon votre commentaire, en regardant bien! Bien sûr, il a foiré mon espace de travail Eclipse, mais il n'en faut pas beaucoup pour cela :)
slinden77
2
Oh, passez à IntelliJ, une fois que vous vous y êtes habitué, Eclipse se sent vraiment vieux;) Du côté positif, le nouveau Android Studio est juste un IntelliJ avec quelques add-ons donc si vous êtes habitué à Intellij, Android Studio vous faire sentir comme chez vous.
Martin Marconcini
2
@ MartínMarconcini enfin, j'ai été obligé de passer à IntelliJ. Je l'aime beaucoup, mais je méprise le fait qu'il est impossible de travailler simultanément sur 2 projets.
slinden77
1
Bienvenue dans le côté obscur;) J'ai trois Android Studio ouverts en ce moment avec 3 projets différents ... je ne sais pas quel est votre problème de plusieurs projets, mais je peux vous assurer que cela fonctionne avec plusieurs projets. :)
Martin Marconcini
22

Vous avez plusieurs options

  1. Vous pouvez mettre un drapeau dans votre classe ou activité. Mettez une variable booléenne dans votre classe et regardez cet indicateur pour savoir si vous avez enregistré le récepteur.

  2. Créez une classe qui étend le récepteur et là, vous pouvez utiliser:

    1. Le modèle singleton n'a qu'une seule instance de cette classe dans votre projet.

    2. Implémentez les méthodes pour savoir si le récepteur est enregistré.

chemalarrea
la source
1
J'ai fait la même chose mais mon récepteur est AppWidgetProvider et je veux recevoir des messages SCREEN_ON_OFF - mais onDisabled () lorsque je désinscrisReceiver (this); - il lève l'exception.
hB0
combinée première et deuxième option, un drapeau dans la classe récepteur, fonctionne très bien
Gaeburider
pouvez-vous me donner un exemple de code car je n'obtiens pas ce que vous faites exactement ... serait d'une grande aide @chemalarrea
TapanHP
11

Vous devez utiliser try / catch:

try {
    if (receiver!=null) {
        Activity.this.unregisterReceiver(receiver);
    }
} catch (IllegalArgumentException e) {
    e.printStackTrace();
}
Mohsen mokhtari
la source
7

Vous pouvez le faire facilement ....

1) créer une variable booléenne ...

private boolean bolBroacastRegistred;

2) Lorsque vous enregistrez votre récepteur de diffusion, réglez-le sur TRUE

...
bolBroacastRegistred = true;
this.registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
....

3) Dans le onPause () faites-le ...

if (bolBroacastRegistred) {
    this.unregisterReceiver(mReceiver);
    bolBroacastRegistred = false
}

Just it, et maintenant, vous ne recevrez plus de message d'erreur d'exception sur onPause ().

Astuce 1: Utilisez toujours l'unregisterReceiver () dans onPause () pas dans onDestroy () Astuce2: N'oubliez pas de définir la variable bolBroadcastRegistred sur FALSE lors de l'exécution de l'unregisterReceive ()

Succès!

Biruel Rick
la source
6

Si vous mettez ceci sur la méthode onDestroy ou onStop. Je pense que lorsque l'activité a été recréée, le MessageReciver n'a pas été créé.

@Override 
public void onDestroy (){
    super.onDestroy();
LocalBroadcastManager.getInstance(context).unregisterReceiver(mMessageReceiver);

}
eloirobe
la source
3

J'ai utilisé Intent pour informer Broadcast Receiver de l'instance du gestionnaire du thread d'activité principal et j'ai utilisé Message pour transmettre un message à l'activité principale.

J'ai utilisé un tel mécanisme pour vérifier si le récepteur de diffusion est déjà enregistré ou non. Parfois, cela est nécessaire lorsque vous enregistrez votre récepteur de diffusion de manière dynamique et que vous ne voulez pas le faire deux fois ou que vous présentez à l'utilisateur si le récepteur de diffusion est en cours d'exécution.

Activité principale:

public class Example extends Activity {

private BroadCastReceiver_example br_exemple;

final Messenger mMessenger = new Messenger(new IncomingHandler());

private boolean running = false;

static class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        running = false;    
        switch (msg.what) {
        case BroadCastReceiver_example.ALIVE:
    running = true;
            ....
            break;
        default:

            super.handleMessage(msg);
        }

    }
    }

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    IntentFilter filter = new IntentFilter();
        filter.addAction("pl.example.CHECK_RECEIVER");

        br_exemple = new BroadCastReceiver_example();
        getApplicationContext().registerReceiver(br_exemple , filter); //register the Receiver
    }

// call it whenever you want to check if Broadcast Receiver is running.

private void check_broadcastRunning() {    
        /**
        * checkBroadcastHandler - the handler will start runnable which will check if Broadcast Receiver is running
        */
        Handler checkBroadcastHandler = null;

        /**
        * checkBroadcastRunnable - the runnable which will check if Broadcast Receiver is running
        */
        Runnable checkBroadcastRunnable = null;

        Intent checkBroadCastState = new Intent();
        checkBroadCastState .setAction("pl.example.CHECK_RECEIVER");
        checkBroadCastState .putExtra("mainView", mMessenger);
        this.sendBroadcast(checkBroadCastState );
        Log.d(TAG,"check if broadcast is running");

        checkBroadcastHandler = new Handler();
        checkBroadcastRunnable = new Runnable(){    

            public void run(){
                if (running == true) {
                    Log.d(TAG,"broadcast is running");
                }
                else {
                    Log.d(TAG,"broadcast is not running");
                }
            }
        };
        checkBroadcastHandler.postDelayed(checkBroadcastRunnable,100);
        return;
    }

.............
}

Récepteur de diffusion:

public class BroadCastReceiver_example extends BroadcastReceiver {


public static final int ALIVE = 1;
@Override
public void onReceive(Context context, Intent intent) {
    // TODO Auto-generated method stub
    Bundle extras = intent.getExtras();
    String action = intent.getAction();
    if (action.equals("pl.example.CHECK_RECEIVER")) {
        Log.d(TAG, "Received broadcast live checker");
        Messenger mainAppMessanger = (Messenger) extras.get("mainView");
        try {
            mainAppMessanger.send(Message.obtain(null, ALIVE));
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    .........

}

}
jarek
la source
3

Personnellement, j'utilise la méthode d'appeler unregisterReceiver et d'avaler l'exception si elle est levée. Je suis d'accord pour dire que c'est moche mais la meilleure méthode actuellement fournie.

J'ai soulevé une demande de fonctionnalité pour obtenir une méthode booléenne pour vérifier si un récepteur est enregistré ajouté à l'API Android. Veuillez le prendre en charge ici si vous souhaitez le voir ajouté: https://code.google.com/p/android/issues/detail?id=73718

ojf
la source
2

J'ai votre problème, j'ai rencontré le même problème dans ma demande. J'appelais registerReceiver () plusieurs fois dans l'application.

Une solution simple à ce problème consiste à appeler le registerReceiver () dans votre classe d'application personnalisée. Cela garantira que votre récepteur de diffusion sera appelé un seul dans l'ensemble de votre cycle de vie de l'application.

public class YourApplication extends Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();

        //register your Broadcast receiver here
        IntentFilter intentFilter = new IntentFilter("MANUAL_BROADCAST_RECIEVER");
        registerReceiver(new BroadcastReciever(), intentFilter);

    }
}
Sameer Ranjan
la source
1

Voici comment je l'ai fait, c'est une version modifiée de la réponse donnée par ceph3us et éditée par slinden77 (entre autres, j'ai supprimé les valeurs de retour des méthodes dont je n'avais pas besoin):

public class MyBroadcastReceiver extends BroadcastReceiver{
    private boolean isRegistered; 

    public void register(final Context context) {
        if (!isRegistered){
            Log.d(this.toString(), " going to register this broadcast receiver");
            context.registerReceiver(this, new IntentFilter("MY_ACTION"));
            isRegistered = true;
        }
    }
    public void unregister(final Context context) {
        if (isRegistered) {            
            Log.d(this.toString(), " going to unregister this broadcast receiver");
            context.unregisterReceiver(this);
            isRegistered = false;
        }
    }
    @Override
    public void onReceive(final Context context, final Intent intent) {        
        switch (getResultCode()){
        //DO STUFF
        }        
    }        
}

Puis sur une classe d'activité:

public class MyFragmentActivity extends SingleFragmentActivity{
    MyBroadcastReceiver myBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        registerBroacastReceiver();       
    }

    @Override
    protected Fragment createFragment(){
        return new MyFragment();
    }

    //This method is called by the fragment which is started by this activity, 
    //when the Fragment is done, we also register the receiver here (if required)
    @Override
    public void receiveDataFromFragment(MyData data) {
        registerBroacastReceiver();
        //Do some stuff                
    }

    @Override
    protected void onStop(){        
        unregisterBroacastReceiver();
        super.onStop();
    }

    void registerBroacastReceiver(){
        if (myBroadcastReceiver == null)
            myBroadcastReceiver = new MyBroadcastReceiver();
        myBroadcastReceiver.register(this.getApplicationContext());
    }

    void unregisterReceiver(){
        if (MyBroadcastReceiver != null)
            myBroadcastReceiver.unregister(this.getApplicationContext());
    }
}
Víctor Gil
la source
1

j'ai mis ce code dans mon activité parentale

List registeredReceivers = new ArrayList <> ();

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    registeredReceivers.add(System.identityHashCode(receiver));
    return super.registerReceiver(receiver, filter);
}

@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
    if(registeredReceivers.contains(System.identityHashCode(receiver)))
    super.unregisterReceiver(receiver);
}
darkwater84
la source
1

Pour moi, ce qui suit a fonctionné:

if (receiver.isOrderedBroadcast()) {
       requireContext().unregisterReceiver(receiver);
}
Benjamin Corben
la source
0

Voici ce que j'ai fait pour vérifier si le diffuseur est déjà enregistré, même si vous fermez votre application (finish ())

La première fois que vous exécutez votre application, envoyez d'abord une diffusion, elle renverra true / false selon que votre diffuseur fonctionne toujours ou non.

Mon diffuseur

public class NotificationReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getExtras() != null && intent.getStringExtra("test") != null){
            Log.d("onReceive","test");
            return;
        }
    }
}

Mon activité principale

// init Broadcaster
private NotificationReceiver nr = new NotificationReceiver();


Intent msgrcv = new Intent("Msg");
msgrcv.putExtra("test", "testing");
boolean isRegistered = LocalBroadcastManager.getInstance(this).sendBroadcast(msgrcv);

if(!isRegistered){
    Toast.makeText(this,"Starting Notification Receiver...",Toast.LENGTH_LONG).show();
    LocalBroadcastManager.getInstance(this).registerReceiver(nr,new IntentFilter("Msg"));
}
janbee
la source
0

Vous pouvez utiliser Dagger pour créer une référence de ce récepteur.

Fournissez-le d'abord:

@Provides
@YourScope
fun providesReceiver(): NotificationReceiver{
    return NotificationReceiver()
}

Puis injecter où vous avez besoin ( en utilisant constructorou sur le terrain injection)

et passez-le simplement à registerReceiver.

Mettez-le également en try/catchbloc aussi.

Mahdi-Malv
la source
-3
if( receiver.isOrderedBroadcast() ){
     // receiver object is registered
}
else{
     // receiver object is not registered
}
Kamta Sahu
la source
1
La diffusion commandée est complètement autre chose regardez ce lien
Vivek Barai
-7

Vérifiez simplement NullPointerException. Si le récepteur n'existe pas, alors ...

try{
    Intent i = new Intent();
    i.setAction("ir.sss.smsREC");
    context.sendBroadcast(i);
    Log.i("...","broadcast sent");
}
catch (NullPointerException e)
{
    e.getMessage();
}
Kasra
la source
1
Comment / où cela lance-t-il un NPE?
DustinB
En fait, il ne génère aucune erreur en cas d'échec. Tristement.
domenukk
2
En fait, il lève une exception IllegalArgumentException
portfoliobuilder