First launch of Activity with Google Maps is very slow

前端 未结 9 1728
礼貌的吻别
礼貌的吻别 2020-12-02 12:37

I want to have SupportMapFragment in one of my Activity. I add this fragment directly to layout xml and this layout set as content view. But when Activity is launched for th

相关标签:
9条回答
  • 2020-12-02 12:49

    I solved it by using MapsInitializer in my Application.onCreate():

    MapsInitializer.initialize(this);
    

    Good results and cleaner (and not hacky) solution!

    0 讨论(0)
  • 2020-12-02 12:50

    I had the same issue and the MapsInitializer trick didn't work for me.

    The best solution to the problem, in my humble opinion, is to load by hand the Map Fragment as described from other users. It's not an hacky solution, you just have to deal yourself with the fragment instance

    mMapFragment = MapFragment.newInstance();
    fragmentManager.beginTransaction().replace(R.id.map_fragment_container, fragment, FRAGMENT_GOOGLEMAPS_TAG).commit();
    
    0 讨论(0)
  • 2020-12-02 12:52

    The reason why the first load takes so long is because the Play Services APIs have to load as seen in log lines:

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

    Unfortunately, the "package" one takes about a second to load and using the MapsInitializer only will get you the "client." So here is a a not so pretty work around: Initialize a dummy map in your main launcher activity.

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

    Now when you load your actual map later on, it shouldn't have to initialize the Play services APIs. This shouldn't cause any delays in your main activity either because the async method executes off the main thread.

    Since you have to do the initialization somewhere no matter what, I think it makes sense to do it right when the app starts, so that when you load an activity that actually needs a map, you do not have to wait at all.

    Note: mDummyMapInitializer must be a MapFragment or SupportMapFragmentand must be added to the activity, or else the Play Services APIs won't be loaded. The getMapAsync method itself must also be called from the main thread.

    0 讨论(0)
  • 2020-12-02 12:57

    I've been fighting this issue too, and have found considerable improvements by doing the following:

    1) Unplug your USB cable (or otherwise disconnect your debugging session) and try it again. Google Maps in an app is much slower when a debugging session is active. Disconnect the debugger and it gets a lot faster... it's still certainly not the fastest, but it's at least acceptable.

    2) Don't call setMapType() unless you've already called getMapType() and confirmed that it's different from what you want to set it to. Multiple calls for the same Map Type will still cause it to reset each time, which can take time.

    3) Add the Map fragment programmatically, similar to what @cYrixmorten posted, but I do it from a background thread started at the end of my onResume(), which then waits 50ms and then runs it on the UI thread. This keeps it from hitting the UI thread right away, so that gives the Activity time to load and display; you should at least be on screen while the map is possibly choking everything up.

    The catch here is that you want to create a new MapFragment instance only once per Activity, not every time the screen orientation is rotated. What I do is call "getFragmentManager().findFragmentById(R.id.mapContainer)", which will either give me the map fragment handle from last time, or a null if this is the first time (in which case I'll create the map fragment and do the FragmentManager.replace() ).

    0 讨论(0)
  • 2020-12-02 12:57

    Similar to the other solutions here, but using RxJava + RxAndroid.

    Just call this snippet from the launcher activity 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());
            }
        });
    
    0 讨论(0)
  • 2020-12-02 13:00

    While this question has been here for years, the problem still persists without a clean and a straight-forward solution. I saw the Rx solution, but it no longer works, so here are my two cents:

    1. My launcher/splashscreen is not backed by a special activity - it is just the background XML with an image (no fancy animations, no routing on the background, just plain loading of resources by the system). This gives me the freedom to lock the main thread as the splash screen doesn't do anything interactive anyway.
    2. The SDK has changed and the function getMapAsync now actually requires you to call it on the main thread. So off-loading it to a Schedulers.io() thread no longer works.
    3. The API of Rx has changed and since version 3.0, null is not allowed to be sent as a "legal" output of an observable, thus I'm sending just true.

    My kotlin solution lives in the main activity, annotated by Hilt as @AndroidEntryPoint in overridden function 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) }
    )
    
    0 讨论(0)
提交回复
热议问题