Android 地图 API v2 上的跳跃标记

Jumping markers on Android Maps API v2

我看到标记在 Android 地图 API v2 的地图上跳来跳去,即使应用程序中没有发生任何事情。

这是该行为的视频:

https://youtu.be/cOUGD0T5Ojs

我期待的

标记应在其最初添加的位置保持静止 lat/long。

哪些步骤会重现问题?

我应该补充一点,我不能总是重现这个。似乎有一段时间一切正常,但是当标记开始跳来跳去时,它们就不会停止。

标记实现细节

加载用于 9 种标记类型(8 个方向 + 无方向)的图标的代码在这里: https://github.com/OneBusAway/onebusaway-android/blob/master/onebusaway-android/src/google/java/org/onebusaway/android/map/googlemapsv2/StopOverlay.java#L175

我正在使用这个可绘制对象: https://github.com/OneBusAway/onebusaway-android/blob/master/onebusaway-android/src/main/res/drawable/map_stop_icon.xml

...这是许多形状 - 这将创建带有白色轮廓和阴影的主要绿色圆圈。然后,我在这个可绘制对象的顶部为 8 个方向中的每一个绘制方向箭头 - 绘制方向的代码在这里:

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

在加载图标的代码中,我在第一次加载时为 9 种图标类型中的每一种缓存了从 BitmapDescriptorFactory.fromBitmap() 返回的 BitmapDescriptor,所以每次加载时都不会这样做标记放在地图上。

我还看到应用程序崩溃到 "Unfortunately, OneBusAway has stopped." 并在让应用程序在地图屏幕上停留几分钟后在 Logcat 中看到此异常:

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)

我在 LG G4 和 Nexus 6 上看到过这个。有关 LG 设备的更多详细信息如下。

这个问题并不总是存在,这让我相信它是在 Android Google Play Services/Maps 的更新过程中引入的。

我已经在 gmaps-api-issues 上为此打开了一个问题,但截至目前还没有回复 post:

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

还有其他人看过吗?有任何修复的想法吗?

编辑

我应该补充一点,我不能总是重现这个。似乎有一段时间一切正常,但是当标记开始跳来跳去时,它们就不会停止。

编辑 2

我在 Github 上创建了一个较小的演示项目,它使用相同的标记实现:

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

不过,我还没有在那里看到同样的问题。

编辑 3

我在 https://github.com/OneBusAway/onebusaway-android/commit/01b35e9a07313a627843819d66b3f6a9bb7e848f 中更改为缓存 Bitmaps 而不是 BitmapDescriptors

我们将看看这是否能解决问题。它是间歇性的,所以我知道的唯一方法是我是否在一段时间内没有再次发现问题。

编辑 4

我仍然看到问题,所以看起来从缓存 BitmapDescriptors 切换到 Bitmaps,并更改为使用 ContextCompat.getDrawable(),没有任何效果。

编辑 5

不确定这是否相关,但发生这种情况时,我也在 Logcat 中看到以下输出:

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)

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.

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

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)

编辑 6 我最近没有看到这个问题,我注意到 LG G4 设备上的 Google Play 服务被撞到了 8.1.15 (2250156-240)。那么,也许它已通过 Google Play 服务的更新得到修复?我稍后再回来报告。

编辑 7 我在 8.1.15 (2250156-240)8.1.18 (2272748-240) 中再次看到了这一点,尽管它看起来不像以前那么糟糕(即更少的标记跳来跳去,而且跳动不太明显)。它似乎主要是在应用程序在后台运行一段时间后恢复应用程序时触发的。如果我终止应用程序然后重新启动它,问题就会消失。

编辑 8 我找到了一种始终如一地重现这一点的方法 - 请参阅: https://code.google.com/p/gmaps-api-issues/issues/detail?id=8455#c2

来自上一期:

  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

LG G4 上的完整视频捕获显示了生产和发行的新步骤: https://youtu.be/oiBoMTPDVrU

我认为这肯定与SupportMapFragment的多个实例同时存在有关。在 v2.0.6 中,当 Activity 被销毁并使用 savedInstanceState 重新创建时(即,当 Activity 在后台运行一段时间或定向后被杀死时,我没有正确处理片段变化)。

v2.0.6 中,我的代码如下所示:

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

所以,在那些情况下,我泄露了 BaseMapFragment 的一个实例(它扩展了 SupportMapFragment)——一个实例已经存在于 FragmentManager 中,但我没有对它的本地引用。当操作栏菜单项被复制到我正在处理的另一个片段时,我实际上注意到了这个问题。

我将代码更改为如下所示,它首先检查 FragmentManager 是否有现有的 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();
        }
    }
    ...
}

master分支的UI流程发生了变化(我不再新开Activity查看路由),所以不能直接在那里测试,如果有问题此代码更改后仍可重现。

然而,我创建了这个分支,它是从 master 分支出来的,但更改为具有与 v2.0.6 相同的 UI 流程,并且包括上述修复片段泄漏:

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

从对这个分支的一些快速测试来看,似乎在方向改变时正确处理片段(即不泄漏片段)可能已经解决了跳跃标记问题 - 在至少使用上面的步骤它不再在 Android 5.1.

的 LG G4 上重现