首次使用Google地图发布活动非常缓慢

时间:2014-10-03 11:33:38

标签: android google-maps

我想在我的一个Activity中安装SupportMapFragment。我将此片段直接添加到布局xml,并将此布局设置为内容视图。但是当Activity第一次启动时,它需要太长时间(超过1秒)。接下来的发布是好的,需要几毫秒。

我试过了:

  • 删除所有初始化
  • 使用MapFragment代替SupportMapFragment
  • 以编程方式添加MapFragment

但没有任何帮助。显示地图没有任何问题或可疑日志。

您是否有任何建议,原因以及如何改进?

修改 我有一个ListView,当用户点击Item时,它会使用MapFragment启动DetailActivity。单击项目后,在DetailActivity出现之前会有明显的延迟。只有onCreate方法,我调用setContentView,运行时间超过1秒。虽然活动在onCreate方法中,但此活动没有可见内容。点击和显示内容之间的这种延迟不是非常用户友好。

谢谢

9 个答案:

答案 0 :(得分:20)

第一次加载需要这么长时间的原因是因为Play服务API必须加载,如日志行所示:

I/Google Maps Android API﹕ Google Play services client version: 6587000
I/Google Maps Android API﹕ Google Play services package version: 6768430

不幸的是,"包"加载和使用MapsInitializer只需要一秒钟就可以获得"客户端。"所以这是一个不那么漂亮的工作:在主启动器活动中初始化一个虚拟地图。

mDummyMapInitializer.getMapAsync(new OnMapReadyCallback() {
  @Override
  public void onMapReady(GoogleMap googleMap) {
    Log.d(TAG, "onMapReady");
  }
});

现在,当您稍后加载实际地图时,它不应该初始化Play服务API。这不应该导致主活动的任何延迟,因为异步方法在主线程上执行。

由于无论如何都必须在某处进行初始化,我认为在应用程序启动时正确执行它是有意义的,这样当您加载实际需要地图的活动时,您根本不需要等待

注意:mDummyMapInitializer必须是MapFragmentSupportMapFragment,必须添加到活动中,否则无法加载Play服务API。还必须从主线程调用getMapAsync方法本身。

答案 1 :(得分:16)

好的,所以我遇到了同样的问题,并且在查看了这个问题后认为没有“好的”解决方案。

我目前的黑客是延迟添加片段,让活动有机会在添加地图之前渲染其他所有内容。

现在,我将地图嵌入为子片段,因此我的代码如下所示:

    // inside onCreateView
    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            if (isAdded()) {
                FragmentManager fm = getChildFragmentManager();
                GoogleMapFragment mapFragment = GoogleMapFragment
                        .newInstance();
                fm.beginTransaction()
                        .replace(R.id.mapContainer, mapFragment).commit();
            }
        }
    }, 1000);

如果直接添加到Activity,它可能如下所示:

    // inside onCreate
    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            if (!isFinishing()) {
                FragmentManager fm = getFragmentManager();
                GoogleMapFragment mapFragment = GoogleMapFragment
                        .newInstance();
                fm.beginTransaction()
                        .replace(R.id.mapContainer, mapFragment).commit();
            }
        }
    }, 1000);

然而,需要在Runnable中进行检查,以确保我们不会尝试将地图添加到某些不存在的Activity或Fragment。

我不喜欢像这样的硬编码延迟,所以如果我想出更好的东西,我会回来。 1秒应该是充足的,甚至可能更少。

答案 2 :(得分:11)

我在Application.onCreate()中使用MapsInitializer解决了它:

MapsInitializer.initialize(this);

良好的结果和更清洁(而不是hacky)的解决方案!

答案 3 :(得分:10)

我也一直在与这个问题作斗争,并通过以下方式找到了相当大的改进:

1)拔下USB电缆(或以其他方式断开调试会话)并再次尝试。调试会话处于活动状态时,应用中的Google地图较慢。断开调试器的连接速度会快得多......它仍然不是最快的,但它至少是可以接受的。

2)除非你已经调用了getMapType()并且确认它与你想要设置的不同,否则不要调用setMapType()。对同一Map类型的多次调用仍会导致每次重置,这可能需要一些时间。

3)以编程方式添加Map片段,类似于@cYrixmorten发布的,但我是从我的onResume()结尾处开始的后台线程开始的,然后等待50ms,然后在UI线程上运行它。这样可以防止它立即触及UI线程,从而为Activity提供加载和显示的时间;你应该至少在屏幕上,而地图可能会阻塞一切。

这里的问题是,您希望每个Activity只创建一个新的MapFragment实例,而不是每次旋转屏幕方向。我所做的是调用“getFragmentManager()。findFragmentById(R.id.mapContainer)”,这将给我上次的地图片段句柄,如果这是第一次给我一个null(在这种情况下我会创建)地图片段并执行FragmentManager.replace())。

答案 4 :(得分:3)

我有一个"主要" activity - 和mapView的活动。当这个activity-with-mapView第一次启动时,它真的很慢。

clocksmith's post给了我一个想法,从一个单独的线程中的主要活动开始初始化。它确实解决了这个问题。

以下是来自" main"的代码。活性:

public void onCreate(Bundle savedInstanceState) {
    ...

    Runnable initMap = () -> {
        BaseApplication.d("Start init mapView");
        MapView mapView = new MapView(MainActivity.this);
        mapView.onCreate(null);
        BaseApplication.d("... done");
    };
    new Thread(initMap).start();
}

从不使用mapView - 它仅用于初始化目的。

这是一个堆栈跟踪 - 仅供参考:

12-09 19:31:54.442 17172-17341/my.app D/XXX: Start init mapView
12-09 19:31:54.525 17172-17341/my.app I/zzy: Making Creator dynamically
12-09 19:31:55.007 17172-17341/my.app D/ChimeraCfgMgr: Reading stored module config
12-09 19:31:55.153 17172-17341/my.app D/ChimeraCfgMgr: Loading module com.google.android.gms.maps from APK /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/MapsModule.apk

12-09 19:31:55.154 17172-17341/my.app D/ChimeraModuleLdr: Loading module APK /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/MapsModule.apk
12-09 19:31:55.262 17172-17341/my.app D/ChimeraFileApk: Primary ABI of requesting process is armeabi-v7a
12-09 19:31:55.271 17172-17341/my.app D/ChimeraFileApk: Classloading successful. Optimized code found.
12-09 19:31:55.316 17172-17341/my.app W/System: ClassLoader referenced unknown path: /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/native-libs/armeabi-v7a

12-09 19:31:55.317 17172-17341/my.app W/System: ClassLoader referenced unknown path: /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/native-libs/armeabi
12-09 19:31:55.618 17172-17341/my.app I/Google Maps Android API: Google Play services client version: 7571000
12-09 19:31:55.630 17172-17341/my.app I/Google Maps Android API: Google Play services package version: 8489438
12-09 19:31:55.969 17172-17341/my.app I/e: Token loaded from file. Expires in: 423267993 ms.
12-09 19:31:55.969 17172-17341/my.app I/e: Scheduling next attempt in 422967 seconds.
12-09 19:31:56.338 17172-17341/my.app D/XXX: ... done

正如我们所看到的,它确实需要花费很多时间......

答案 5 :(得分:1)

对我而言,因为我使用它的速度比1秒慢:

mapFragment.getMap();

然后我改为:

 mapFragment.getMapAsync(new OnMapReadyCallback() {
        @Override
        public void onMapReady(GoogleMap googleMap) {
            map = googleMap;
        }
 });

使用getMapAsync()不会阻止ui,因此您的活动将在地图之前加载。它仍然很慢,但就我的目的而言,只显示加载消息就可以了。

答案 6 :(得分:1)

与此处的其他解决方案类似,但使用RxJava + RxAndroid。

只需从启动器活动onCreate调用此代码段即可。

Observable.fromCallable(new Callable<Void>() {
    @Override
    public Void call() {
        MapView mapView = new MapView(LaunchActivity.this); // Replace LaunchActivity with appropriate activity's name
        mapView.onCreate(null);
        return null;
    }
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(
    new Action1<Void>() {
        @Override
        public void call(Void ignorable) {
            Log.i("Google Maps", "Initialised Google Maps.");
        }
    }, new Action1<Throwable>() {
        @Override
        public void call(Throwable ignorable) {
            Log.w("Google Maps", "[EXPECTED] Initialized Google Maps but got: " + ignorable.getMessage());
        }
    });

答案 7 :(得分:0)

我遇到了同样的问题,但MapsInitializer的技巧对我不起作用。

以我的拙见,解决该问题的最佳方法是按照其他用户的描述手动加载Map Fragment。这不是一个棘手的解决方案,您只需要处理片段实例

mMapFragment = MapFragment.newInstance();
fragmentManager.beginTransaction().replace(R.id.map_fragment_container, fragment, FRAGMENT_GOOGLEMAPS_TAG).commit();

答案 8 :(得分:0)

这个问题已经存在多年了,但是如果没有一个干净而直接的解决方案,问题仍然存在。我看到了Rx解决方案,但是它不再起作用,所以这里是我的两分钱:

  1. 我的启动器/启动画面没有特殊活动的支持-它只是带有图像的后台XML(没有奇特的动画,没有在后台进行路由,只是系统简单地加载了资源)。这使我可以自由锁定主线程,因为初始屏幕无论如何都不会进行任何交互。
  2. SDK已更改,现在实际上getMapAsync函数要求您在主线程上调用。因此,将其卸载到Schedulers.io()线程将不再起作用。
  3. Rx的API已更改,并且自版本3.0起,不允许null作为可观察对象的“合法”输出发送,因此我只发送了true

我的kotlin解决方案存在于主要活动中,在替代函数@AndroidEntryPoint中被Hilt标注为onCreate(savedInstanceState: Bundle?)

val mainThread = AndroidSchedulers.mainThread()
Observable.fromCallable {
    val mapView = MapView(this)
    mapView.onCreate(null)
    true
}.subscribeOn(mainThread).observeOn(mainThread).subscribe(
    // on success
    { Log.i("MAPS", "Initialized Google Maps.") },
    // on error
    { Log.w("MAPS", "Warming up of Google Maps failed: " + it.message) }
)