Erreur d'exception du récepteur non enregistré?

112

Dans ma console développeur, les gens continuent de signaler une erreur que je ne peux reproduire sur aucun de mes téléphones. Une personne a laissé un message disant qu'elle l'obtient lorsqu'elle essaie d'ouvrir l'écran des paramètres de mon service de batterie. Comme vous pouvez le voir dans l'erreur, il est indiqué que le récepteur n'est pas enregistré.

java.lang.RuntimeException: Unable to stop service .BatteryService@4616d688:  java.lang.IllegalArgumentException: Receiver not registered: com.app.notifyme.BatteryService$BatteryNotifyReceiver@4616d9d0
at android.app.ActivityThread.handleStopService(ActivityThread.java:3164)
at android.app.ActivityThread.access$3900(ActivityThread.java:129)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2173)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:4701)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: Receiver not registered:com..BatteryService$BatteryNotifyReceiver@4616d9d0
at android.app.ActivityThread$PackageInfo.forgetReceiverDispatcher(ActivityThread.java:805)
at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:859)
at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:331)
at com.app.notifyme.BatteryService.onDestroy(BatteryService.java:128)
at android.app.ActivityThread.handleStopService(ActivityThread.java:3150)

Je m'inscris est dans mon onCreate

@Override
public void onCreate(){
    super.onCreate();
    SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
    IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    filter.addAction(Intent.ACTION_POWER_CONNECTED);
    filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
    registerReceiver(batteryNotifyReceiver,filter);
    pref.registerOnSharedPreferenceChangeListener(this);
}

Se désinscrire dans onDestroy et aussi avec un écouteur de préférences

    @Override
public void onDestroy(){
    super.onDestroy();
    unregisterReceiver(batteryNotifyReceiver);

}

et c'est mon récepteur dans le service

private final class BatteryNotifyReceiver extends BroadcastReceiver {

    boolean connected;
    @Override
    public void onReceive(Context context, Intent intent) {

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 
        SharedPreferences.Editor edit = prefs.edit();

            updatePreferences(prefs);

        level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);



        if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)){
            connected = true;
        }else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)){
            connected = false;
        }else if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)){

                if(level < lastLevel){
                    if(level > 40){
                        edit.putBoolean("first", false).commit();
                        edit.putBoolean("second", false).commit();
                        edit.putBoolean("third", false).commit();
                       edit.putBoolean("fourth",false).commit();                            
                        edit.putBoolean("fifth", false).commit();
                    }
                    if(level == 40){
                        if(!first){
                        notification(context,battColor,battBlink,battVib,battSound);

                        edit.putBoolean("first", true).commit();
                        }
                    }else if(level == 30){
                        if(!second){
                        notification(context,battColor,battBlink,battVib,battSound);

                        edit.putBoolean("second", true).commit();
                        }
                    }else if(level == 20){
                        if(!third){
                        notification(context,battColor,battBlink,battVib,battSound);

                        edit.putBoolean("third", true).commit();
                        }
                    }else if(level == 15){
                        if(!fourth){
                        notification(context,battColor,battBlink,battVib,battSound);

                        edit.putBoolean("fourth", true).commit();
                        }
                    }else if(level == 5){
                        if(!fifth){
                        notification(context,battColor,battBlink,battVib,battSound);

                        edit.putBoolean("fifth", true).commit();
                        }
                    }
                lastLevel = temp;
            }
        }           

        Intent i = new Intent(context,BatteryNotifyReceiver.class);
        context.startService(i);
    }       
}

une idée pourquoi ils obtiendraient cette erreur?

tyczj
la source

Réponses:

207

La racine de votre problème se trouve ici:

 unregisterReceiver(batteryNotifyReceiver);

Si le récepteur n'était pas déjà enregistré (probablement dans le code que vous n'avez pas inclus dans ce message) ou n'était pas enregistré, appelez les unregisterReceiverlancers IllegalArgumentException. Dans votre cas, vous devez simplement mettre un try / catch spécial pour cette exception et l'ignorer (en supposant que vous ne pouvez pas ou ne voulez pas contrôler le nombre de fois que vous appelez unregisterReceiverle même recevier).

inazaruk
la source
31
Est-il possible pour la plate-forme Android elle-même de faire cela? J'avais un code similaire à celui ci-dessus, mais sans aucun autre code qui annulerait l'enregistrement du service, et j'obtiens la même exception.
electrichead
2
J'ai le même problème @electrichead, je ne peux pas voir où le récepteur serait désenregistré avant onDestroy () à moins qu'Android ne le fasse pour moi quelque part ....
user1088166
@ user1088166, essayez de remplacer la méthode onStop () pour votre activité. Ajoutez-y un try catch (IllegalArgumentException e) et désenregistrez volontairement votre diffusion - voyez si cela aide (vient de remarquer que c'est un commentaire vieux de 3 ans :))
Nactus
Dans mon cas, le coupable était l'initialisation paresseuse - j'ai sauté l'enregistrement car l'objet n'a pas été initialisé, il a donc été initialisé plus tard et j'ai oublié l'enregistrement du récepteur.
Boris Treukhov
@electrichead non, il ne le fait pas pour vous, il vous suffit de choisir la bonne méthode: LocalBroadcastManager.getInstance(context).unregisterReceiver()oucontext.unregisterReceiver()
user924
25

Soyez prudent, lorsque vous vous inscrivez par

LocalBroadcastManager.getInstance(this).registerReceiver()

vous ne pouvez pas vous désinscrire par

 unregisterReceiver()

Tu dois utiliser

LocalBroadcastManager.getInstance(this).unregisterReceiver()

ou l'application plantera, connectez-vous comme suit:

09-30 14: 00: 55.458 19064-19064 / com.jialan.guangdian.view E / AndroidRuntime: FATAL EXCEPTION: main Process: com.jialan.guangdian.view, PID: 19064 java.lang.RuntimeException: Impossible d'arrêter le service com.google.android.exoplayer.demo.player.PlayService@141ba331: java.lang.IllegalArgumentException: Récepteur non enregistré: com.google.android.exoplayer.demo.player.PlayService$PlayStatusReceiver@19538584 at android.app.ActivityThread. handleStopService (ActivityThread.java:2941) à android.app.ActivityThread.access $ 2200 (ActivityThread.java:148) sur android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1395) sur android.os.Handler.dispatchMessage (Handler.java:102) sur android.os.Looper.loop (Looper.java:135) sur android.app.ActivityThread.main (ActivityThread.java:5310) à java.lang.reflect.Method.invoke (méthode native) à java.lang.reflect.Method.invoke (Method.java:372) sur com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:901) sur com.android.internal.os.ZygoteInit.main (ZygoteInit.java:696) Causé par: java.lang.IllegalArgumentException : Récepteur non enregistré: com.google.android.exoplayer.demo.player.PlayService$PlayStatusReceiver@19538584 sur android.app.LoadedApk.forgetReceiverDispatcher (LoadedApk.java:769) sur android.app.Contextpl.unregisterReceiver (ContextImplava)1794) sur android.content.ContextWrapper.unregisterReceiver (ContextWrapper.java:510) sur com.google.android.exoplayer.demo.player.PlayService.onDestroy (PlayService.java:542) sur android.app.ActivityThread.handleStopService (ActivityThread .java: 2924) sur android.app.ActivityThread.access $ 2200 (ActivityThread.java:148) sur android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1395) sur android.os.Handler.dispatchMessage (Handler.java:102) sur android.os.Looper.loop (Looper.java:135) sur android.app.ActivityThread.main (ActivityThread.java:5310) sur java. lang.reflect.Method.invoke (Méthode native) sur java.lang.reflect.Method.invoke (Method.java:372) sur com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:901) sur com.android.internal.os.ZygoteInit.main (ZygoteInit.java:696) 

Chuanhang.gu
la source
C'est une vraie réponse à ce problème! N'oubliez pas si vous avez déjà enregistré un récepteur mondial ou local
user924
23

Utilisez ce code partout pour unregisterReceiver:

if (batteryNotifyReceiver != null) {
    unregisterReceiver(batteryNotifyReceiver);
    batteryNotifyReceiver = null;
}
xnagyg
la source
6
Cela m'arrive encore. Mieux vaut utiliser try-catch.
palindrom
2
Try catch statement is better
Rowland Mtetezi
16

Comme mentionné dans d'autres réponses, l'exception est levée car chaque appel à registerReceiverne correspond pas exactement à un appel à unregisterReceiver. Pourquoi pas?

An Activityn'a pas toujours un onDestroyappel correspondant pour chaque onCreateappel. Si le système manque de mémoire, votre application est expulsée sans appeler onDestroy.

Le bon endroit pour passer un registerReceiverappel est dans l' onResumeappel et unregisterReceiverdans onPause. Cette paire d'appels est toujours mise en correspondance. Consultez le diagramme du cycle de vie des activités pour plus de détails. http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

Votre code deviendrait:

SharedPreferences mPref
IntentFilter mFilter;

@Override
public void onCreate(){
    super.onCreate();
    mPref = PreferenceManager.getDefaultSharedPreferences(this);
    mFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    filter.addAction(Intent.ACTION_POWER_CONNECTED);
    filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
 }

@Override
public void onResume() {
    registerReceiver(batteryNotifyReceiver,mFilter);
    mPref.registerOnSharedPreferenceChangeListener(this);
}

@Override
public void onPause(){
     unregisterReceiver(batteryNotifyReceiver, mFilter);
     mPref.unregisterOnSharedPreferenceChangeListener(this);
}
Curmudgeonlybumbly
la source
Mais cela entraînerait la surcharge de l'enregistrement et de la désinscription beaucoup plus de fois. Je ne suis pas sûr du montant des frais généraux, mais il y en aura certainement.
Aman Deep Gautam
2
Êtes-vous vraiment en train de suggérer de laisser une exception jetant un gestionnaire invalide en place, car cela coûterait quelques cycles de processeur pour le supprimer?
Curmudgeonlybumbly
11

EDIT: C'est la réponse pour inazaruk et electrichead ... J'avais rencontré un problème similaire à eux et j'ai découvert ce qui suit ...

Il existe un bogue de longue date pour ce problème ici: http://code.google.com/p/android/issues/detail?id=6191

On dirait qu'il a commencé autour d'Android 2.1 et est présent dans toutes les versions d'Android 2.x depuis. Je ne sais pas si c'est toujours un problème sous Android 3.x ou 4.x.

Quoi qu'il en soit, cet article de StackOverflow explique comment contourner le problème correctement (cela ne semble pas pertinent par l'URL mais je le promets)

Pourquoi le clavier-slide plante-t-il mon application?

Justin
la source
8

J'ai utilisé un bloc try - catch pour résoudre temporairement le problème.

// Unregister Observer - Stop monitoring the underlying data source.
        if (mDataSetChangeObserver != null) {
            // Sometimes the Fragment onDestroy() unregisters the observer before calling below code
            // See <a>http://stackoverflow.com/questions/6165070/receiver-not-registered-exception-error</a>
            try  {
                getContext().unregisterReceiver(mDataSetChangeObserver);
                mDataSetChangeObserver = null;
            }
            catch (IllegalArgumentException e) {
                // Check wether we are in debug mode
                if (BuildConfig.IS_DEBUG_MODE) {
                    e.printStackTrace();
                }
            }
        }
Rowland Mtetezi
la source
3

Déclarez le receveur comme nul, puis placez les méthodes register et unregister respectivement dans onResume () et onPause () de l'activité.

@Override
protected void onResume() {
    super.onResume();
    if (receiver == null) {
        filter = new IntentFilter(ResponseReceiver.ACTION_RESP);
        filter.addCategory(Intent.CATEGORY_DEFAULT);
        receiver = new ResponseReceiver();
        registerReceiver(receiver, filter);
    }
}      

@Override
protected void onPause() {
    super.onPause();
    if (receiver != null) {
        unregisterReceiver(receiver);
        receiver = null;
    }
}
Sagar D
la source
0

Lorsque le composant UI qui enregistre le BR est détruit, le BR l'est aussi. Par conséquent, lorsque le code arrive à la désinscription, le BR peut avoir déjà été détruit.

ousanmaz
la source
0

Pour tous ceux qui rencontreront ce problème et qui ont essayé tout ce qui a été suggéré et que rien ne fonctionne toujours, c'est ainsi que j'ai trié mon problème, au lieu de le faire, LocalBroadcastManager.getInstance(this).registerReceiver(...) j'ai d'abord créé une variable locale de type LocalBroadcastManager,

private LocalBroadcastManager lbman;

Et utilisé cette variable pour effectuer l'enregistrement et le désenregistrement sur le récepteur de diffusion, c'est-à-dire

lbman.registerReceiver(bReceiver);

et

lbman.unregisterReceiver(bReceiver);
nada
la source
0

Supposons que votre broadcastReceiversoit défini comme ceci:

private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {

        // your code

    }
};

Si vous utilisez LocalBroadcastdans une activité, voici comment vous désinscrire:

LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);

Si vous utilisez LocalBroadcastdans un Fragment, voici comment vous désinscrire:

LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver);

Si vous utilisez la diffusion normale dans une activité, voici comment vous désinscrire:

unregisterReceiver(broadcastReceiver);

Si vous utilisez la diffusion normale dans un fragment, voici comment vous désinscrire:

getActivity().unregisterReceiver(broadcastReceiver);
zeeshan
la source