Odbiorca niezarejestrowany błąd wyjątku?


112

W mojej konsoli programisty ludzie zgłaszają błąd, którego nie mogę odtworzyć na żadnym moim telefonie. Jedna osoba zostawiła wiadomość, że dostaje ją, gdy próbują otworzyć ekran ustawień mojej usługi baterii. Jak widać po błędzie jest napisane, że odbiornik nie jest zarejestrowany.

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)

Rejestruję się w moim 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);
}

Wyrejestruj się z onDestroy, a także z odbiornikiem preferencji

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

}

a to jest mój odbiornik w serwisie

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);
    }       
}

masz pojęcie, dlaczego otrzymają ten błąd?

Odpowiedzi:


207

Tutaj znajduje się źródło problemu:

 unregisterReceiver(batteryNotifyReceiver);

Jeśli odbiorca był już wyrejestrowany (prawdopodobnie w kodzie, którego nie umieściłeś w tym poście) lub nie był zarejestrowany, to wywołaj unregisterReceiverrzuca IllegalArgumentException. W twoim przypadku musisz po prostu ustawić specjalną try / catch dla tego wyjątku i zignorować go (zakładając, że nie możesz lub nie chcesz kontrolować, ile razy dzwonisz unregisterReceiverdo tego samego odbiorcy).


31
Czy sama platforma Android może to zrobić? Miałem kod podobny do powyższego, ale bez żadnego innego kodu, który wyrejestrowałby usługę, i dostaję ten sam wyjątek.
elektryczna

2
Mam ten sam problem @electrichead, nie widzę, gdzie odbiornik zostałby wyrejestrowany przed onDestroy (), chyba że Android robi to gdzieś za mnie ....
user1088166

@ user1088166, spróbuj zastąpić metodę onStop () dla swojej aktywności. Tam dodaj próbny catch (IllegalArgumentException e) i celowo wyrejestruj swoją transmisję - zobacz, czy to pomaga (właśnie zauważyłem, że jest to komentarz od 3 lat :))
Nactus

W moim przypadku winowajcą była leniwa inicjalizacja - pominąłem rejestrację, ponieważ obiekt nie został zainicjowany, więc został zainicjowany później i zapomniałem rejestracji odbiorcy.
Boris Treukhov

@electrichead nie, to nie robi tego za Ciebie, wystarczy wybrać odpowiednią metodę: LocalBroadcastManager.getInstance(context).unregisterReceiver()lubcontext.unregisterReceiver()
user924

25

Uważaj, rejestrując się do

LocalBroadcastManager.getInstance(this).registerReceiver()

nie możesz się wyrejestrować

 unregisterReceiver()

musisz użyć

LocalBroadcastManager.getInstance(this).unregisterReceiver()

lub aplikacja ulegnie awarii, zaloguj się w następujący sposób:

09-30 14: 00: 55.458 19064-19064 / com.jialan.guangdian.view E / AndroidRuntime: KRYTYCZNY WYJĄTEK: główny Proces: com.jialan.guangdian.view, PID: 19064 java.lang.RuntimeException: Nie można zatrzymać usługi com.google.android.exoplayer.demo.player.PlayService@141ba331: java.lang.IllegalArgumentException: odbiorca niezarejestrowany: com.google.android.exoplayer.demo.player.PlayService$PlayStatusReceiver@19538584 na android.app.ActivityThread. handleStopService (ActivityThread.java:2941) na android.app.ActivityThread.access $ 2200 (ActivityThread.java:148) pod adresem android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1395) pod adresem android.os.Handler.dispatchMessage (Handler.java:102) pod adresem android.os.Looper.loop (Looper.java:135) pod adresem android.app.ActivityThread.main (ActivityThread.java:5310) pod adresem java.lang.reflect.Method.invoke (metoda macierzysta) pod adresem java.lang.reflect.Method.invoke (Method.java:372) pod adresem com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:901) pod adresem com.android.internal.os.ZygoteInit.main (ZygoteInit.java:696) Przyczyna: java.lang.IllegalArgumentException : Odbiorca niezarejestrowany: com.google.android.exoplayer.demo.player.PlayService$PlayStatusReceiver@19538584 na android.app.LoadedApk.forgetReceiverDispatcher (LoadedApk.java:769) na android.app.ContextImpl.unregisterReceiver (ContextImpl.unregisterReceiver:1794) pod adresem android.content.ContextWrapper.unregisterReceiver (ContextWrapper.java:510) pod adresem com.google.android.exoplayer.demo.player.PlayService.onDestroy (PlayService.java:542) pod adresem android.app.ActivityThread.handleStopService (ActivityThread .java: 2924) pod adresem android.app.ActivityThread.access $ 2200 (ActivityThread.java:148) pod adresem android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1395) pod adresem android.os.Handler.dispatchMessage (Handler.java:102) pod adresem android.os.Looper.loop (Looper.java:135) pod adresem android.app.ActivityThread.main (ActivityThread.java:5310) pod adresem java. lang.reflect.Method.invoke (metoda rodzima) w java.lang.reflect.Method.invoke (Method.java:372) w com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:901) pod adresem com.android.internal.os.ZygoteInit.main (ZygoteInit.java:696) 


To prawdziwa odpowiedź na ten problem! Tylko nie zapomnij, jeśli wcześniej zarejestrowałeś globalny lub lokalny odbiornik
user924

23

Użyj tego kodu wszędzie, aby wyrejestrować odbiorcę:

if (batteryNotifyReceiver != null) {
    unregisterReceiver(batteryNotifyReceiver);
    batteryNotifyReceiver = null;
}

6
Wciąż mi się to przytrafia. Lepiej użyj try-catch.
palindrom

2
Wypróbuj wyrażenie catch jest lepsze
Rowland Mtetezi

16

Jak wspomniano w innych odpowiedziach, wyjątek jest generowany, ponieważ każde wywołanie funkcji registerReceivernie jest dopasowywane przez dokładnie jedno wywołanie do unregisterReceiver. Dlaczego nie?

An Activitynie zawsze ma pasujące onDestroywywołanie dla każdego onCreatepołączenia. Jeśli w systemie zabraknie pamięci, Twoja aplikacja zostanie usunięta bez dzwonienia onDestroy.

Właściwe miejsce do wykonania registerReceiverpołączenia to onResumewezwanie i unregisterReceiverwejście onPause. Ta para wywołań jest zawsze dopasowana. Aby uzyskać więcej informacji, zobacz diagram cyklu życia działania. http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

Twój kod zmieniłby się na:

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);
}

Ale spowodowałoby to dużo więcej kosztów związanych z rejestracją i wyrejestrowaniem. Nie jestem pewien, jakie są narzuty, ale na pewno będzie.
Aman Deep Gautam

2
Czy naprawdę sugerujesz pozostawienie wyjątku rzucającego nieprawidłową procedurę obsługi, ponieważ usunięcie go kosztowałoby kilka cykli procesora?
Curmudgeonlybumbly

11

EDYCJA: To jest odpowiedź na inazaruk i electrichead ... Napotkałem podobny problem do nich i dowiedziałem się, co następuje ...

Ten problem istnieje od dawna tutaj: http://code.google.com/p/android/issues/detail?id=6191

Wygląda na to, że zaczęło się od Androida 2.1 i od tego czasu było obecne we wszystkich wydaniach Androida 2.x. Nie jestem pewien, czy nadal jest to problem w systemie Android 3.x lub 4.x.

W każdym razie ten post StackOverflow wyjaśnia, jak poprawnie obejść problem (nie wygląda to na odpowiedni adres URL, ale obiecuję, że tak jest)

Dlaczego keyboard-slide powoduje awarię mojej aplikacji?


8

Użyłem bloku try - catch, aby tymczasowo rozwiązać problem.

// 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();
                }
            }
        }

3

Zadeklaruj odbiorcę jako null, a następnie umieść register i wyrejestruj metody odpowiednio w onResume () i onPause () działania.

@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;
    }
}

0

Kiedy komponent UI, który rejestruje BR jest zniszczony, tak samo jest BR. Dlatego, gdy kod zostanie wyrejestrowany, BR może już zostać zniszczona.


0

Dla każdego, kto napotka ten problem i wypróbował wszystko, co zostało zasugerowane, i nic nadal nie działa, tak posortowałem mój problem, zamiast robić LocalBroadcastManager.getInstance(this).registerReceiver(...) , najpierw utworzyłem lokalną zmienną typu LocalBroadcastManager,

private LocalBroadcastManager lbman;

I użył tej zmiennej do przeprowadzenia rejestracji i wyrejestrowania w odbiorniku

lbman.registerReceiver(bReceiver);

i

lbman.unregisterReceiver(bReceiver);

0

Załóżmy, że twój broadcastReceiverjest zdefiniowany w ten sposób:

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

        // your code

    }
};

Jeśli używasz LocalBroadcastw działaniu, wyrejestrujesz się w następujący sposób:

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

Jeśli używasz LocalBroadcastwe fragmencie, wyrejestrujesz się w następujący sposób:

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

Jeśli używasz normalnej transmisji w działaniu, wyrejestrujesz się w następujący sposób:

unregisterReceiver(broadcastReceiver);

Jeśli używasz normalnej transmisji we fragmencie, wyrejestrujesz się w ten sposób:

getActivity().unregisterReceiver(broadcastReceiver);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.