wake-up-neo.net

Der erste Start von Activity mit Google Maps ist sehr langsam

Ich möchte SupportMapFragment in einer meiner Aktivitäten haben. Ich füge dieses Fragment direkt zur Layout-XML hinzu und dieses Layout als Inhaltsansicht. Wenn Activity jedoch zum ersten Mal gestartet wird, dauert es zu lange (über 1 Sekunde). Die nächsten Starts sind in Ordnung und dauern einige Millisekunden.

Ich habe es versucht:

  • eventuelle Initialisierung entfernen
  • verwenden Sie MapFragment anstelle von SupportMapFragment
  • mapFragment programmgesteuert hinzufügen

aber nichts half. Die Karte wird ohne Probleme oder verdächtiges Protokoll angezeigt. 

Haben Sie einen Vorschlag, was es bewirkt und wie Sie es verbessern können? 

edit: Ich habe eine ListView und wenn der Benutzer auf Item klickt, wird DetailActivity mit MapFragment gestartet. Nach dem Klicken auf das Element tritt eine merkliche Verzögerung auf, bevor DetailActivity angezeigt wird. Nur die Methode onCreate, bei der ich setContentView aufrufe, läuft über 1 Sekunde. Während sich die Aktivität in der onCreate-Methode befindet, ist kein sichtbarer Inhalt dieser Aktivität vorhanden. Diese Verzögerung zwischen dem Klicken und dem Anzeigen von Inhalten ist nicht sehr benutzerfreundlich.

Vielen Dank

37
nonahex

Der Grund, warum das erste Laden so lange dauert, liegt darin, dass die Play Services-APIs wie in den Protokollzeilen geladen werden müssen:

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

Leider dauert es etwa eine Sekunde, bis das "Paket" geladen ist. Wenn Sie nur MapsInitializer verwenden, erhalten Sie den "Client". Hier ist eine nicht ganz so hübsche Lösung: Initialisieren Sie eine Dummy-Karte in Ihrer Hauptstartaktivität.

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

Wenn Sie Ihre aktuelle Karte später laden, sollte die API für die Play-Dienste nicht initialisiert werden müssen. Dies sollte auch keine Verzögerungen in Ihrer Hauptaktivität verursachen, da die async-Methode außerhalb des Hauptthreads ausgeführt wird. 

Da Sie die Initialisierung in jedem Fall an einem beliebigen Ort durchführen müssen, ist es meiner Meinung nach sinnvoll, sie beim Start der App richtig auszuführen. Wenn Sie also eine Aktivität laden, die tatsächlich eine Karte benötigt, müssen Sie nicht warten.

Hinweis: mDummyMapInitializer muss eine MapFragment oder SupportMapFragment sein und muss der Aktivität hinzugefügt werden, andernfalls werden die Play Services-APIs nicht geladen. Die getMapAsync-Methode selbst muss auch vom Haupt-Thread aus aufgerufen werden.

19
clocksmith

Ok, also hatte ich gerade das gleiche Problem und denke, nachdem ich diese Frage angesehen habe, gibt es keine "schöne" Lösung.

Mein aktueller Hack besteht darin, das Hinzufügen des Fragments zu verzögern, damit die Aktivität vor dem Hinzufügen der Karte alles andere rendern kann.

Nun binde ich die Karte als Kinderfragment ein, so dass mein Code so aussieht:

    // 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);

wenn Sie direkt zu Activity hinzufügen, könnte es so aussehen:

    // 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);

Trotzdem ist eine Überprüfung im Runable erforderlich, um sicherzustellen, dass wir nicht versuchen, die Map zu einer nicht vorhandenen Aktivität oder einem Fragment hinzuzufügen.

Ich bin kein Fan von hart codierten Verzögerungen wie diesem. Ich werde also wiederkommen, wenn mir etwas Besseres einfällt. 1 Sekunde sollte jedoch reichlich sein und könnte wahrscheinlich noch weniger sein. 

15
cYrixmorten

Ich habe es gelöst, indem ich MapsInitializer in meinem Application.onCreate () verwendet:

MapsInitializer.initialize(this);

Gute Ergebnisse und eine sauberere (und nicht hackige) Lösung!

10
Fidel Montesino

Ich habe auch dieses Problem bekämpft und mit folgenden Maßnahmen erhebliche Verbesserungen gefunden:

1) Trennen Sie Ihr USB-Kabel (oder trennen Sie Ihre Debug-Sitzung anderweitig) und versuchen Sie es erneut. Google Maps in einer App ist viel langsamer, wenn eine Debugging-Sitzung aktiv ist. Trennen Sie den Debugger und es wird viel schneller ... er ist zwar immer noch nicht der schnellste, aber zumindest akzeptabel.

2) Rufen Sie setMapType () nicht auf, es sei denn, Sie haben bereits getMapType () aufgerufen und bestätigt, dass es sich von dem unterscheidet, für den Sie es festlegen möchten. Bei mehreren Anrufen für denselben Kartentyp werden diese immer noch zurückgesetzt, was einige Zeit in Anspruch nehmen kann.

3) Fügen Sie das Map-Fragment programmgesteuert hinzu, ähnlich dem, was @cYrixmorten gepostet hat, aber ich mache es aus einem Hintergrund-Thread, der am Ende meines onResume () -Starts gestartet wurde, der dann 50ms wartet und dann auf dem UI-Thread ausführt. Dadurch wird verhindert, dass der UI-Thread sofort getroffen wird, sodass die Aktivität Zeit zum Laden und Anzeigen hat. Sie sollten zumindest auf dem Bildschirm sein, während die Karte möglicherweise alles aufwirft.

Der Haken dabei ist, dass Sie eine neue MapFragment-Instanz nur einmal pro Aktivität erstellen möchten, nicht bei jeder Drehung der Bildschirmausrichtung. Was ich mache, ist "getFragmentManager (). FindFragmentById (R.id.mapContainer)", der mir entweder das Map-Fragment-Handle vom letzten Mal gibt oder eine Null, wenn dies das erste Mal ist (in diesem Fall werde ich das erstellen.) das Kartenfragment und führen Sie das FragmentManager.replace ()) aus.

8
Wookie

Ich habe eine "Hauptaktivität" - und eine Aktivität mit mapView. Wenn diese Aktivität mit mapView zum ersten Mal gestartet wird, ist sie wirklich langsam.

clocksmith's post gab mir die Idee, die Initialisierung von der Hauptaktivität aus in einem separaten Thread zu starten. Und es löst wirklich das Problem.

Hier ist mein Code aus "Hauptaktivität":

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 wird nie verwendet - es dient nur zur Initialisierung.

Und hier ist ein Stack-Trace - nur zur Information:

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

Wie wir sehen können, braucht es wirklich viel Zeit ...

3
cVoronin

Für mich war es viel langsamer als 1 Sekunde, da ich es verwendete:

mapFragment.getMap();

Dann habe ich gewechselt zu:

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

Wenn Sie getMapAsync () verwenden, wird die Benutzeroberfläche nicht blockiert, sodass Ihre Aktivität vor der Map geladen wird. Es ist immer noch langsam, aber für meine Zwecke war es okay, nur eine Lademeldung zu zeigen.

1
sagits

Ähnlich wie die anderen Lösungen hier, jedoch mit RxJava + RxAndroid.

Nennen Sie dieses Snippet einfach aus der Starteraktivität 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());
        }
    });
1
Joao Sousa

Ich hatte das gleiche Problem und der MapsInitializer-Trick funktionierte nicht für mich.

Die beste Lösung für das Problem besteht meiner bescheidenen Meinung nach darin, das Kartenfragment von Hand zu laden, wie von anderen Benutzern beschrieben. Es ist keine harte Lösung, Sie müssen sich nur mit der Fragment-Instanz befassen

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