illegal state exception when trying to change a ma

2019-04-28 16:13发布

问题:

I am writing an android app and I have markers on a Google Map that should be changed to a position that I receive by GCM. I have a global static List in which I save the marker together with an id and the position. I can change the positions of the Objects in the list in my GCMBaseIntentService when I receive a message, but when I want to execute the following code:

    mMarker.setPosition(new LatLng(member.getLatitude(),member.getLongitude()));

I get the following Exception:

    01-13 18:52:38.118: E/AndroidRuntime(7605): FATAL EXCEPTION: IntentService[GCMIntentService-1041237551356-1]
    01-13 18:52:38.118: E/AndroidRuntime(7605): java.lang.IllegalStateException: Not on the main thread
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at maps.am.r.b(Unknown Source)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at maps.ar.d.b(Unknown Source)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at maps.y.ae.addMarker(Unknown Source)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.google.android.gms.maps.internal.IGoogleMapDelegate$Stub.onTransact(IGoogleMapDelegate.java:167)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at android.os.Binder.transact(Binder.java:279)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a.addMarker(Unknown Source)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.google.android.gms.maps.GoogleMap.addMarker(Unknown Source)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.example.MapViewActivity.showMember(MapViewActivity.java:529)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.example.MapViewActivity.updateMember(MapViewActivity.java:417)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.example.MapViewActivity.updateMembers(MapViewActivity.java:410)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.example.pushnotifications.GCMIntentService.onMessage(GCMIntentService.java:75)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at com.google.android.gcm.GCMBaseIntentService.onHandleIntent(GCMBaseIntentService.java:223)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at android.os.Handler.dispatchMessage(Handler.java:99)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at android.os.Looper.loop(Looper.java:130)
    01-13 18:52:38.118: E/AndroidRuntime(7605):     at android.os.HandlerThread.run(HandlerThread.java:60)

I tried adding a button to the map that refreshes all markers (with exactly the same code) and that works fine. So somehow the problem is that the GCM is executed in a different thread or something like that and it seems that I cannot change markers from a different thread...

I tried an ugly solution: To refresh all markers every second. But creating a thread from the MapViewActivity gives the same exception:

private Thread MarkerUpdater = new Thread(
    new Runnable() {
        public void run() {
            while(true) {
                updateMembers();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    });

Maybe someone has an idea how a nice solution could look like?

回答1:

You need to get the handler for your main UI thread and post a runnable there.

Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable(){ 
   // your UI code here 
}

Any variables you reference from your service in your Runnable will need to be final. Since you said you are sharing the data as global/static, you should be okay.



回答2:

This might be useful for someone else: I just had a very similar problem of markers not updating when receiving a GCM message. The solution is indeed to put the marker update code in a main loop handler, but for some reason it does matter what you put in that run()-method.

TLDR

Just putting the marker.setPosition(lat, lon)-call in the runnable wasn't enough. I had to put all of my marker handling code in the runnable (finding the markers, checking if they have a position, setting the new position, updating the map camera).

In detail

The problem

  • My app was receiving location updates from someone else via GCM.
  • With every location update, the coresponding marker had to be updated.
  • The first GCM was received by my application, but the next ones weren't.
  • No crashes, and the application was fully responsive, just no more GCM messages received after the first one.

What I discovered

  • My first thought was that the GCM server was just being slow.
  • But: While monitoring all console output I saw debug messages from GCM showing that the messages were arriving on the device, but not in the app!

What worked for me

  • Make sure all Google Maps code that needs to run when receiving the GCM is in a Runnable on the MainLooper.

Conslusion

  • It does make sense to put all GM code in a runnable, but seeing that there was no error output and the app was completely responsive it was very hard to discover that solution.


回答3:

Use my code:

    private class UpdateThread extends AsyncTask<Void, Void, Void>
    {
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            //...
            mMarker.setPosition(new LatLng(member.getLatitude(),member.getLongitude()));
        }
        @Override
        protected Void doInBackground(Void... params) {

            while(true)
            {
                try {
                    //...
                    publishProgress();

                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    UpdateThread updateThread = new UpdateThread();
    updateThread.execute();


回答4:

Try this,

Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
         @Override
         public void run() {
              //your code
         }
});