Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jumping markers on Android Maps API v2

I'm seeing markers jump around on the map on Android Maps API v2 even when nothing is happening in the app.

Here's a video of the behavior:

https://youtu.be/cOUGD0T5Ojs

What I expect

Markers should remain stationary at their originally added lat/long.

What steps will reproduce the problem?

    1. Build, install, and run the v2.0.6 tag of OneBusAway:
      • a. git clone https://github.com/OneBusAway/onebusaway-android.git
      • b. git checkout v2.0.6
      • c. gradlew installObaGoogleDebug
      • d. adb shell am start -n com.joulespersecond.seattlebusbot/org.onebusaway.android.ui.HomeActivity
    1. Browse to any supported city (e.g., Seattle or Tampa), and watch the green bus stop markers jump around on the map

I should add that I can't always reproduce this. It seems like everything works fine for a time, but then when the markers start jumping around they don't stop.

Marker Implementation Details

The code that loads the icons used for the 9 marker types (8 directions + no direction) is here: https://github.com/OneBusAway/onebusaway-android/blob/master/onebusaway-android/src/google/java/org/onebusaway/android/map/googlemapsv2/StopOverlay.java#L175

I'm using this drawable: https://github.com/OneBusAway/onebusaway-android/blob/master/onebusaway-android/src/main/res/drawable/map_stop_icon.xml

...which is a number of shapes - this creates the main green circle with the white outline and the drop shadoes. Then, I'm drawing the direction arrow on top of this drawable for each of the 8 directions - code for drawing directions is here:

https://github.com/OneBusAway/onebusaway-android/blob/master/onebusaway-android/src/google/java/org/onebusaway/android/map/googlemapsv2/StopOverlay.java#L208

In the code to load the icons, I'm caching the BitmapDescriptor returned from BitmapDescriptorFactory.fromBitmap() for each of the 9 icon types on first load, so this isn't done each time a marker is put on the map.

I also saw the app crash to "Unfortunately, OneBusAway has stopped." and saw this exception in Logcat after letting the app sit on the map screen for a few minutes:

08-10 16:40:02.422  15843-15929/com.joulespersecond.seattlebusbot E/AndroidRuntime﹕ FATAL EXCEPTION: GLThread 8614
    Process: com.joulespersecond.seattlebusbot, PID: 15843
    java.lang.IllegalArgumentException: Comparison method violates its general contract!
            at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:831)
            at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:449)
            at java.util.ComparableTimSort.mergeCollapse(ComparableTimSort.java:372)
            at java.util.ComparableTimSort.sort(ComparableTimSort.java:178)
            at java.util.ComparableTimSort.sort(ComparableTimSort.java:142)
            at java.util.Arrays.sort(Arrays.java:1957)
            at java.util.Collections.sort(Collections.java:1864)
            at com.google.maps.api.android.lib6.gmm6.n.bl.a(Unknown Source)
            at com.google.maps.api.android.lib6.gmm6.n.l.a(Unknown Source)
            at com.google.maps.api.android.lib6.gmm6.n.l.b(Unknown Source)
            at com.google.maps.api.android.lib6.gmm6.n.cv.f(Unknown Source)
            at com.google.maps.api.android.lib6.gmm6.n.cv.run(Unknown Source)

I've seen this on an LG G4 and Nexus 6. More details on LG device is below.

  • LG G4 LS991 with Android 5.1 (LS991ZV4)

  • Google Play Services client library version = compile 'com.google.android.gms:play-services-maps:7.5.0' and compile 'com.google.android.gms:play-services-maps:7.8.0'

  • Google Play Services version on the device - Google Play Services 7.8.99 (2134222-440)

  • Android SDK Version: compileSdkVersion 21 buildToolsVersion "21.1.2"

This issue hasn't always existed, which makes me believe it was introduced during an update to Android Google Play Services/Maps at some point.

I've opened an issue for this on gmaps-api-issues as well, but no response as of this post:

https://code.google.com/p/gmaps-api-issues/issues/detail?id=8455

Has anyone else seen this? Any ideas for fixes?

EDIT

I should add that I can't always reproduce this. It seems like everything works fine for a time, but then when the markers start jumping around they don't stop.

EDIT 2

I've created a smaller demo project here on Github that uses the same marker implementation:

https://github.com/barbeau/maps-demo

However, I haven't yet seen the same problem there.

EDIT 3

I've changed to caching Bitmaps instead of BitmapDescriptors in https://github.com/OneBusAway/onebusaway-android/commit/01b35e9a07313a627843819d66b3f6a9bb7e848f.

We'll see if this fixes the problem. It's intermittent, so the only way I'll know is if I don't see the problem again for some period of time.

EDIT 4

I'm still seeing the problem, so looks like switching from caching BitmapDescriptors to Bitmaps, and changing to using ContextCompat.getDrawable(), didn't have any effect.

EDIT 5

Not sure if this is related, but I'm also seeing the following output in Logcat when this happens:

09-01 10:46:00.339 9278-9278/? E/libEGL﹕ validate_display:255 error 3008 (EGL_BAD_DISPLAY) 09-01 10:46:00.339 9278-9278/? E/libEGL﹕ validate_display:255 error 3008 (EGL_BAD_DISPLAY)

and

9-01 10:46:00.069 9278-9278/? W/ResourcesManager﹕ Asset path '/system/framework/com.google.android.maps.jar' does not exist or contains no resources.

and

09-01 10:46:16.019 1137-4311/? W/ActivityManager﹕ Scheduling restart of crashed service com.google.android.gms/.usagereporting.service.UsageReportingService in 1000ms 09-01 10:46:16.019 1137-4311/? W/ActivityManager﹕ Scheduling restart of crashed service com.google.android.gms/.icing.service.IndexService in 11000ms

and

09-01 10:48:38.609 5402-26676/? E/SQLiteDatabase﹕ Error inserting context_name=8 end_time=1441118918490 context_family=7 module_id=com.google.android.contextmanager.module.PowerConnectionModule version=1 sync_state_mod_time_millis=1441118918532 start_time=1441118643058 sync_state=0 context_id=9680c4f4-789a-4d86-acbf-43d2098e89b8 time_type=3 proto_blob=[B@28265a3 android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: context.context_id (code 2067) at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method) at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:790) at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:926) at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86) at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1581) at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1451) at com.google.android.contextmanager.q.ak.a(SourceFile:405) at com.google.android.contextmanager.q.ak.b(SourceFile:380) at com.google.android.contextmanager.q.ak.a(SourceFile:346) at com.google.android.contextmanager.q.ak.b(SourceFile:373) at com.google.android.contextmanager.g.a.j.a(SourceFile:58) at com.google.android.contextmanager.g.a.a.run(SourceFile:52) at com.google.android.contextmanager.g.i.handleMessage(SourceFile:214) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.os.HandlerThread.run(HandlerThread.java:61)

EDIT 6 I haven't seen this issue lately, and I noticed that Google Play services on the LG G4 device was bumped to 8.1.15 (2250156-240). So, maybe it was fixed with an update of Google Play services? I'll report back again later.

EDIT 7 I've seen this again in 8.1.15 (2250156-240) and 8.1.18 (2272748-240), although it doesn't seem nearly as bad as it used to be (i.e., fewer markers jump around, and the jumping is less noticeable). It seems to be triggered mainly when resuming the app when its been in the background for a while. If I kill the app and then start it fresh, the problem disappears.

EDIT 8 I found a way to consistently reproduce this - see: https://code.google.com/p/gmaps-api-issues/issues/detail?id=8455#c2

From above issue:

  1. Build, install, and run the v2.0.6 tag of OneBusAway:

    a. git clone https://github.com/OneBusAway/onebusaway-android.git

    b. git checkout v2.0.6

    c. gradlew installObaGoogleDebug

    d. adb shell am start -n com.joulespersecond.seattlebusbot/org.onebusaway.android.ui.HomeActivity

  2. Hold the device in a portrait orientation

  3. If you're not physically located in Seattle or Tampa (or any of the supported regions), you'll need to go to "Settings->Your region" and manually set the region. After doing this, scroll the map to the region (or select "Take me there" when prompted).
  4. Tap on a bus stop on the map
  5. Tap on the 3 dots "more" button next to arrival time (or tap on arrival in list in sliding panel)
  6. Tap on option "Show route on map"
  7. After the route loads on the map, change the device orientation to landscape.
  8. Watch the markers jump around after the map reloads

Full video capture on LG G4 that shows new steps to produce and issue: https://youtu.be/oiBoMTPDVrU

like image 313
Sean Barbeau Avatar asked Aug 28 '15 17:08

Sean Barbeau


1 Answers

I think this is definitely related to multiple instances of the SupportMapFragment existing simultaneously. In v2.0.6 I wasn't handling fragments correctly when the Activity was destroyed and recreated with a savedInstanceState (i.e., when the Activity was killed after being in background for a while, or on orientation change).

In v2.0.6, my code looked something like this:

public class HomeActivity ...{

    BaseMapFragment mMapFragment;

    ...

    private void showMap() {
        FragmentManager fm = getSupportFragmentManager();
        if (mMapFragment == null) {
                mMapFragment = BaseMapFragment.newInstance();
                fm.beginTransaction()
                    .add(R.id.main_fragment_container, mMapFragment)
                    .commit();
        }
    }
    ...
}

So, I was leaking an instance of BaseMapFragment (which extends SupportMapFragment) in those cases - an instance already existed in the FragmentManager, but I didn't have a local reference to it. I actually noticed this issue when Action Bar menu items were being duplicated for another fragment I was handling similarly.

I changed the code to look something like this, which first checks to see if the FragmentManager has an existing BaseMapFragment:

public class HomeActivity ...{

    BaseMapFragment mMapFragment;

    ...

    private void showMap() {
        FragmentManager fm = getSupportFragmentManager();            
        // First check to see if an instance of BaseMapFragment already exists            
        mMapFragment = (BaseMapFragment) fm.findFragmentByTag(BaseMapFragment.TAG);

        if (mMapFragment == null) {
            mMapFragment = BaseMapFragment.newInstance();
            fm.beginTransaction()
                    .add(R.id.main_fragment_container, mMapFragment, BaseMapFragment.TAG)
                    .commit();
        }
    }
    ...
}

The UI flow has changed in the master branch (I'm no longer opening a new Activity to view the route), so I can't directly test there if the problem is still reproducible after this code change.

However, I created this branch, which is branched from master but changed to have the same UI flow as v2.0.6, and includes the above fix for the fragment leak:

https://github.com/CUTR-at-USF/onebusaway-android/tree/jumpingMarkersTest

From some quick testing with this branch, it seems that handling the fragments correctly on orientation change (i.e., not leaking the fragment) may have fixed the jumping markers issue - at least using the steps above it no longer reproduces on a LG G4 with Android 5.1.

like image 83
Sean Barbeau Avatar answered Sep 26 '22 18:09

Sean Barbeau