Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.IllegalArgumentException: Receiver not registered

Tags:

android

I have TrackingService component to track the location of the buses in my city based on Crowdsourcing. The TrackingService is operating in the background, then the data is transmitted to the server. I have an Map Activity to display the location of the buses, the user selected in the MainActivity( as Filter).

The background TrackingService is started in the MainActivity when the app launches.

I notifing the map activity about the updated lcoation with the aid of the BroadcastReceiver as in the code below. The data is being retrieved to the the map activity but I am facing problem to unregister my bReceiver. I want to unregister the broadcast when the app goes in the background or when the user presses the back button but I am getting the error below:

How can I fix it?

Error:

08-27 22:43:04.594: E/AndroidRuntime(19588): FATAL EXCEPTION: main
08-27 22:43:04.594: E/AndroidRuntime(19588): Process: com.bustracker, PID: 19588
08-27 22:43:04.594: E/AndroidRuntime(19588): java.lang.RuntimeException: Unable to stop activity {com.bustracker/com.bustracker.Map}: java.lang.IllegalArgumentException: Receiver not registered: com.bustracker.Map$1@2483d256
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4156)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4219)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.ActivityThread.access$1500(ActivityThread.java:177)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1502)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.os.Handler.dispatchMessage(Handler.java:102)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.os.Looper.loop(Looper.java:145)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.ActivityThread.main(ActivityThread.java:5944)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at java.lang.reflect.Method.invoke(Native Method)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at java.lang.reflect.Method.invoke(Method.java:372)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1389)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1184)
08-27 22:43:04.594: E/AndroidRuntime(19588): Caused by: java.lang.IllegalArgumentException: Receiver not registered: com.bustracker.Map$1@2483d256
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.LoadedApk.forgetReceiverDispatcher(LoadedApk.java:822)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:2038)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:528)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at com.bustracker.Map.onStop(Map.java:418)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1275)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.Activity.performStop(Activity.java:6493)
08-27 22:43:04.594: E/AndroidRuntime(19588):    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4151)
08-27 22:43:04.594: E/AndroidRuntime(19588):    ... 10 more

TrackingService class:

public class TrackingService extends Service implements
        LocationListener {
    public double pLong;
    public double pLat;
    ...
        @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        detectLocation();
        return START_STICKY;
    }
    private void detectLocation() {
        lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 30 * 1000, 0,
                this);
    }
    @Override
    public void onLocationChanged(Location location) {

        if (location != null) {
        pLong = location.getLongitude();
        pLat = location.getLatitude();

        Intent intent = new Intent(Map.RECEIVE_latLng);
        intent.putExtra("location",location);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
           .....

     }  

}

Map activity:

    public class Map extends FragmentActivity implements OnMapReadyCallback   {
   public static final String RECEIVE_latLng = "com.bustracker.RECEIVE_latLng";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.map);

        LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(RECEIVE_latLng);
        bManager.registerReceiver(bReceiver, intentFilter);

    }


    private BroadcastReceiver bReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equals(RECEIVE_latLng)) {
                              Location location = intent.getParcelableExtra("location");
             double lng = location.getLongitude();
             double lat = location.getLatitude();
             LatLng ll = new LatLng(lat, lng);
             MarkerOptions markerOpt = new MarkerOptions().title("My Location")
                        .position(ll);
             System.out.println("ABC map: "+ lat + " ; " + lng);
             myLocatMarker = map.addMarker(markerOpt);
            }
          }
        };      
      }
@Override
protected void onStop() {
    super.onStop();
    unregisterReceiver(bReceiver);      
}
like image 274
Mr Asker Avatar asked Aug 27 '15 21:08

Mr Asker


4 Answers

If you register in onCreate(), you have to unregister in onDestroy(). If you want to unregister in onStop() you have to register in onStart().

Have a look at the activity lifecycle here http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

The reason for this is that onStop() is called when the Activity goes into the background, but is not necessarily destroyed. When the Activity comes back to the foreground onStart() is called, but not onCreate() so the BroadcastReceiver isn't re-registered. Then when the Activity goes back into the background, onStop() tries to unregister again, but the receiver has not been registered.

You also need to use the LocalBroadcastManager to unregister your receiver if you used it to register it like so:

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

LocalBroadcastManager is a class from the support library:

Helper to register for and send broadcasts of Intents to local objects within your process.

This is different from the same methods on Context which allow system-wide broadcasts.

Also see a similar question/answer here.

like image 145
ci_ Avatar answered Nov 17 '22 18:11

ci_


(Of course, if you want) You may register or unregister them in onStop() and onResume() just wrap it with try-catch:

@Override
public void onResume(){
    super.onResume()
    try{ 
        getActivity().registerReceiver(receiver,filter); 
    }catch (Exception e){
        // already registered
    }
}

Or

@Override
public void onStop(){
    try{ 
        getActivity().unregisterReceiver(receiver);  
    }catch (Exception e){
        // already unregistered
    }
    super.onStop()
}
like image 27
Farid Avatar answered Nov 17 '22 19:11

Farid


Keep in mind that you must register and unregister on the same context. For example, don't register w/ the application context and unregister w/ the activity context.

Don't do this

getApplicationContext().registerReceiver(myReceiver, myIntentFilter);
unregisterReceiver(myReceiver);

Do this instead (inside an activity)

registerReceiver(myReceiver, myIntentFilter);
unregisterReceiver(myReceiver);

I normally register inside onPostResume() or onResume() and unregister in onPause().

Examples:

protected void onPostResume() {
    super.onPostResume();

    registerReceiver(myReceiver, myIntentFilter);
}

protected void onPause() {
    unregisterReceiver(myReceiver);

    //called after unregistering
    super.onPause();
}
like image 7
Markymark Avatar answered Nov 17 '22 20:11

Markymark


You have to register a broadcast receiver in onResume() and unregistered it in onPause() because they will be called for sure before the fragment or the activity is being destroyed for any reasons. If you register a broadcast receiver in onStart() and unregistered it in onStop(), still works fine, but it has a caveat though. If you use onStart() and onStop(), you don't have guarantee that broadcast receiver will be unregistered in onStop() method b.c of Android Lifecycle

For instance: There are two activities (A & B). When moving from activity A to activity B, the onStart() and onResume() of activity B may be called before onStop() of activity A, and the wrong activity which is in this case activity A's method onStop() may catch broadcast receiver of activity B and unregistered it, and then when activity B is being closed or invisible, its onStop() method is called to unregistere the broadcast receiver that has already being unregistered by onStop() method of Activity A, and that causes a crash.

In simple term, even though activity A is invisible, its onStop() method still can listen events of activity B. Therefore, if you want to listen only for events when your activity is visible then use onPause() method to unregister a broadcast receiver.

Generally speaking:

register on onResume() unregister onPause() guaranteed

register on onStart() unregister onStop() not guaranteed due to activity lifecycle.

like image 4
Josi Avatar answered Nov 17 '22 20:11

Josi