96

I'm making use of the new Android Google Maps API.

I create an activity which includes a MapFragment. In the activity onResume I set the markers into the GoogleMap object and then define a bounding box for the map which includes all of the markers.

This is using the following pseudo code:

LatLngBounds.Builder builder = new LatLngBounds.Builder();
while(data) {
   LatLng latlng = getPosition();
   builder.include(latlng);
}
CameraUpdate cameraUpdate = CameraUpdateFactory
   .newLatLngBounds(builder.build(), 10);
map.moveCamera(cameraUpdate);

The call to map.moveCamera() causes my application to crash with the following stack:

Caused by: java.lang.IllegalStateException: 
    Map size should not be 0. Most likely, layout has not yet 

    at maps.am.r.b(Unknown Source)
    at maps.y.q.a(Unknown Source)
    at maps.y.au.a(Unknown Source)
    at maps.y.ae.moveCamera(Unknown Source)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$Stub
        .onTransact(IGoogleMapDelegate.java:83)
    at android.os.Binder.transact(Binder.java:310)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a
        .moveCamera(Unknown Source)
    at com.google.android.gms.maps.GoogleMap.moveCamera(Unknown Source)
    at ShowMapActivity.drawMapMarkers(ShowMapActivity.java:91)
    at ShowMapActivity.onResume(ShowMapActivity.java:58)
    at android.app.Instrumentation
        .callActivityOnResume(Instrumentation.java:1185)
    at android.app.Activity.performResume(Activity.java:5182)
    at android.app.ActivityThread
        .performResumeActivity(ActivityThread.java:2732)

If - instead of the newLatLngBounds() factory method I use newLatLngZoom() method then the same trap does not occur.

Is the onResume the best place to draw the markers onto the GoogleMap object or should I be drawing the markers and setting the camera position somewhere else?

JJD
  • 50,076
  • 60
  • 203
  • 339
Lee
  • 3,996
  • 3
  • 33
  • 37

20 Answers20

133

You can use simple newLatLngBounds method in OnCameraChangeListener. All will be working perfectly and you don't need to calculate screen size. This event occurs after map size calculation (as I understand).

Example:

map.setOnCameraChangeListener(new OnCameraChangeListener() {

    @Override
    public void onCameraChange(CameraPosition arg0) {
        // Move camera.
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 10));
        // Remove listener to prevent position reset on camera move.
        map.setOnCameraChangeListener(null);
    }
});
Paul Annekov
  • 3,193
  • 3
  • 19
  • 31
  • 1
    This works because setOnCameraChangeListener is guaranteed to run at least once, after the Map has undergone layout? Is this future proof? – Glenn Bech Aug 07 '13 at 07:45
  • @GlennBech no, Google Maps API doesn't say you anything about this behaviour. But practically it always called. – Paul Annekov Aug 07 '13 at 20:12
  • 3
    @SteelRat Thats why I would be against using the solution. You never know when google change this, or even if it works that way on all android versions or devices. – Glenn Bech Aug 07 '13 at 21:03
  • @GlennBech you can check android version before using this code. I think that it is intuitively clear that onCameraChange will run at least once after map is loaded. – Paul Annekov Aug 08 '13 at 05:33
  • 1
    @SteelRat I agree, but it might be called more than once in the lifetime of the activity? I am just making the argument that even if it works in this case, its not a very elegant solution. I think the OnGlobalLayoutListener/Treelistener is a more correct way to go about this. – Glenn Bech Aug 08 '13 at 21:42
  • @GlennBech you are removing listener to disable listen after first call. You can compare number of code lines between my solution and OnGlobalLayoutListener/Treelistener :). I think that my solution looks more attractive than OnGlobalLayoutListener. – Paul Annekov Aug 09 '13 at 08:37
  • 4
    maybe adding **map.moveCamera(CameraUpdateFactory.scrollBy(1, 1));** after the code above will make the trick? – M.Y. Aug 22 '13 at 16:04
  • perfect, was so easy! – TharakaNirmana Dec 09 '13 at 04:34
  • This feels so hacky, I wonder why there is not a better way to position the camera before the map is shown? – theblang Feb 13 '14 at 15:14
  • Perfect solution after 1 hour search. – A J Nov 06 '14 at 10:07
  • This first positions the camera at the incorrect position (0.0, 0.0 - somewhere near Africa) and then to the correct position causing flicker. – John Patterson Feb 17 '15 at 12:45
  • great answer and great research @SteelRat. I think this should be marked as correct. – Ankit Bansal Feb 19 '15 at 11:32
  • 3
    `setOnCameraChangeListener` is now deprecated – osrl Dec 12 '16 at 13:37
  • "This event occurs after map size calculation (as I understand)", unfortunately, no, not quranteed to be called after the layout is ready – Farid Oct 29 '20 at 08:54
58

Those answers are just fine, but I would go for a different approach, a simpler one. If the method only works after the map is layed out, just wait for it:

map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
    @Override
    public void onMapLoaded() {
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
    }
});
Leandro
  • 691
  • 5
  • 7
  • 1
    I found after much experimentation that I needed to implement a *combination* of this answer and Daniele's above. Sometimes the map had not been loaded yet, sometimes layout had not completed yet. I couldn't move the camera as desired until BOTH of those happened. – RobP Jun 14 '14 at 19:19
  • 2
    Warning for this method, from the docs: "This event will not fire if the map never loads due to connectivity issues, *or if the map is continuously changing and never completes loading due to the user constantly interacting with the map*." (emphasis mine). – stkent Dec 15 '14 at 18:38
  • 1
    This delays longer than necessary because it waits for the map tiles to load at the wrong position before moving to the correct position. – John Patterson Feb 17 '15 at 12:46
  • 1
    Most of the time it takes anything between 3-10 seconds to fire which is unacceptable. – Nima Jun 04 '15 at 04:36
  • This is the place to move camera. I would suggest use map.animateCamera rather than map.moveCamera – Bharat Dodeja May 04 '16 at 13:44
52

OK I worked this out. As documented here that API can't be used pre-layout.

The correct API to use is described as:

Note: Only use the simpler method newLatLngBounds(boundary, padding) to generate a CameraUpdate if it is going to be used to move the camera after the map has undergone layout. During layout, the API calculates the display boundaries of the map which are needed to correctly project the bounding box. In comparison, you can use the CameraUpdate returned by the more complex method newLatLngBounds(boundary, width, height, padding) at any time, even before the map has undergone layout, because the API calculates the display boundaries from the arguments that you pass.

To fix the problem I calculated my screen size and provided the width and height to

public static CameraUpdate newLatLngBounds(
    LatLngBounds bounds, int width, int height, int padding)

This then allowed me to specify the bounding box pre-layout.

JJD
  • 50,076
  • 60
  • 203
  • 339
Lee
  • 3,996
  • 3
  • 33
  • 37
  • 19
    But what if the map doesn't take up the entire screen? – theblang Feb 13 '14 at 15:16
  • In my case I just had assumed a larger screen than was in effect, and needed to calculate the padding more carefully so it in fact was *not* too large. A much simpler reason. – rfay Oct 29 '14 at 19:21
  • 2
    Can you show how to calculate it based on the screen size? – Daniel Gomez Rico Jun 23 '15 at 03:12
  • This doesn't crash but the width and height to be used are close to pure guesses, the exact area to be drawn is really unpredictable. – Vince Aug 15 '19 at 23:04
34

The solution is simpler than that....

// Pan to see all markers in view.
try {
    this.gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
} catch (IllegalStateException e) {
    // layout not yet initialized
    final View mapView = getFragmentManager()
       .findFragmentById(R.id.map).getView();
    if (mapView.getViewTreeObserver().isAlive()) {
        mapView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @SuppressLint("NewApi")
            // We check which build version we are using.
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                    mapView.getViewTreeObserver()
                        .removeGlobalOnLayoutListener(this);
                } else {
                    mapView.getViewTreeObserver()
                        .removeOnGlobalLayoutListener(this);
                }
                gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
            }
        });
    }
}

Catch IllegalStateException and use the global listener on the view instead. Setting the bound size in advance (pre-layout) using the other methods means you have to compute the size of THE VIEW, not the screen device. They only match if you go full screen with your map and do not use fragments.

JJD
  • 50,076
  • 60
  • 203
  • 339
Daniele Segato
  • 12,314
  • 6
  • 62
  • 88
  • Till now I've been using your little snippet, but it still fail... the correct answer is the first one :-) – cesards Feb 28 '13 at 09:05
  • 1
    How/when it fail? never had an issue with this. The first answer just pass an hardcoded size to it as a fallback. "It works" as in "it doesn't crash anymore" but it is not doing the same thing. – Daniele Segato Oct 18 '13 at 17:42
  • @andrea.spot I do not remember why I said simplier. I guess the accepted solution has been edited in the meanwhile. The *current* accepted solution assume your map take all the screen, which is not always true. If you do not fall in that case you either need to know the size of the map or use my method or something similar. – Daniele Segato Jul 17 '15 at 14:49
  • also check the answer below by Xingang Huang, it adds a bit to it. – Daniele Segato Jul 17 '15 at 14:51
15

This is my fix, just wait untill the map is loaded in that case.

final int padding = getResources().getDimensionPixelSize(R.dimen.spacing);    

try {
    mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
} catch (IllegalStateException ise) {

    mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {

        @Override
        public void onMapLoaded() {
            mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
        }
    });
}
pl3kn0r
  • 347
  • 4
  • 9
15

The accepted answer wouldn't work for me because I had to use the same code in various places of my app.

Instead of waiting the camera to change, I created a simple solution based on Lee's suggestion of specifying the map size. This is in case your map is the size of the screen.

// Gets screen size
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
// Calls moveCamera passing screen size as parameters
map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, 10));

Hope it helps someone else!

Teo Inke
  • 5,928
  • 4
  • 38
  • 37
14

Adding and removing markers can be done pre-layout completion, but moving the camera cannot (except using newLatLngBounds(boundary, padding) as noted in the OP's answer).

Probably the best place to perform an initial camera update is using a one-shot OnGlobalLayoutListener as shown in Google's sample code, e.g. see the following excerpt from setUpMap() in MarkerDemoActivity.java:

// Pan to see all markers in view.
// Cannot zoom to bounds until the map has a size.
final View mapView = getSupportFragmentManager()
    .findFragmentById(R.id.map).getView();
if (mapView.getViewTreeObserver().isAlive()) {
    mapView.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {
        @SuppressLint("NewApi") // We check which build version we are using.
        @Override
        public void onGlobalLayout() {
            LatLngBounds bounds = new LatLngBounds.Builder()
                    .include(PERTH)
                    .include(SYDNEY)
                    .include(ADELAIDE)
                    .include(BRISBANE)
                    .include(MELBOURNE)
                    .build();
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
              mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
              mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
        }
    });
}
JJD
  • 50,076
  • 60
  • 203
  • 339
stephent
  • 1,355
  • 15
  • 29
  • I created a [gradlefied version of the Maps sample project](https://github.com/johnjohndoe/GoogleMapsV2Sample). – JJD Oct 10 '14 at 13:29
  • I get this error on orientation change. I also found this from sample code but this didn't work for me. I still get the same error. – osrl Dec 12 '16 at 13:09
8

The accepted answer is, as pointed out in the comments, a little hacky. After implementing it I noticed an above-acceptable amount of IllegalStateException logs in analytics. The solution I used is to add an OnMapLoadedCallback in which the CameraUpdate is performed. The callback is added to the GoogleMap. After your map fully loads the camera update will be performed.

This does cause the map to briefly show the zoomed out (0,0) view before performing the camera update. I feel that this is more acceptable than causing crashes or relying on undocumented behavior though.

theblang
  • 10,215
  • 9
  • 69
  • 120
7
 map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));

use this it worked for mee

Ramesh Bhupathi
  • 408
  • 7
  • 10
4

In very rare cases, MapView layout is finished, but GoogleMap layout is not finished, ViewTreeObserver.OnGlobalLayoutListener itself can't stop the crash. I saw the crash with Google Play services package version 5084032. This rare case may be caused by the dynamic change of the visibility of my MapView.

To solve this problem, I embedded GoogleMap.OnMapLoadedCallback in onGlobalLayout(),

if (mapView.getViewTreeObserver().isAlive()) {
    mapView.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        @SuppressWarnings("deprecation")
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
                mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            try {
                map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 5));
            } catch (IllegalStateException e) {
                map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
                    @Override
                    public void onMapLoaded() {
                        Log.d(LOG_TAG, "move map camera OnMapLoadedCallback");
                        map.moveCamera(CameraUpdateFactory
                            .newLatLngBounds(bounds, 5));
                    }
                });
            }
        }
    });
}
JJD
  • 50,076
  • 60
  • 203
  • 339
Alpha Huang
  • 1,297
  • 1
  • 12
  • 15
  • Interesting: If I refactor `mapView.getViewTreeObserver()` into a **local variable** the following **error** occurs: *"IllegalStateException: This ViewTreeObserver is not alive, call getViewTreeObserver() again"*. Did you try this? – JJD Oct 06 '14 at 13:47
  • If mapView.getViewTreeObserver().isAlive() is happen to be false I'd put one more fallback for map.setOnMapLoadedCallback() – Kurovsky Apr 11 '16 at 17:17
4

I used different approach which works for recent versions of Google Maps SDK (9.6+) and based on onCameraIdleListener. As I see so far it's callback method onCameraIdle called always after onMapReady. So my approach looks like this piece of code (considering it put in Activity):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // set content view and call getMapAsync() on MapFragment
}

@Override
public void onMapReady(GoogleMap googleMap) {
    map = googleMap;
    map.setOnCameraIdleListener(this);
    // other initialization stuff
}

@Override
public void onCameraIdle() {
    /* 
       Here camera is ready and you can operate with it. 
       you can use 2 approaches here:

      1. Update the map with data you need to display and then set
         map.setOnCameraIdleListener(null) to ensure that further events
         will not call unnecessary callback again.

      2. Use local boolean variable which indicates that content on map
         should be updated
    */
}
Viacheslav
  • 5,443
  • 1
  • 29
  • 36
4

I've created a way to combine the two callbacks: onMapReady and onGlobalLayout into one single observable which will emit only when both the events have been triggered.

https://gist.github.com/abhaysood/e275b3d0937f297980d14b439a8e0d4a

Abhay Sood
  • 480
  • 2
  • 6
  • 20
  • 1
    It's in fact the cleanest way to solve this. I'm doing this for long time now and no error is returned in analytics even on 1k users per day app. +1 – Skyle Dec 16 '19 at 14:08
2

Ok I'm facing same issue. I have my fragment with my SupportmapFragment, ABS, and navigation drawer. What I did was:

public void resetCamera() {

    LatLngBounds.Builder builderOfBounds = new LatLngBounds.Builder();
    // Set boundaries ...
    LatLngBounds bounds = builderOfBounds.build();
    CameraUpdate cu;
    try{
        cu = CameraUpdateFactory.newLatLngBounds(bounds,10);
        // This line will cause the exception first times 
        // when map is still not "inflated"
        map.animateCamera(cu); 
        System.out.println("Set with padding");
    } catch(IllegalStateException e) {
        e.printStackTrace();
        cu = CameraUpdateFactory.newLatLngBounds(bounds,400,400,0);
        map.animateCamera(cu);
        System.out.println("Set with wh");
    }

    //do the rest...
}

And, by the way, I'm calling resetCamera() from onCreateView after inflating and before returning.
What this does is catch the exception first time (while map "gets a size" as a way of saying it...) and then, other times I need to reset the camera, map already has size and does it through padding.

Issue is explained in documentation, it says:

Do not change the camera with this camera update until the map has undergone layout (in order for this method to correctly determine the appropriate bounding box and zoom level, the map must have a size). Otherwise an IllegalStateException will be thrown. It is NOT sufficient for the map to be available (i.e. getMap() returns a non-null object); the view containing the map must have also undergone layout such that its dimensions have been determined. If you cannot be sure that this has occured, use newLatLngBounds(LatLngBounds, int, int, int) instead and provide the dimensions of the map manually.

I think it's a pretty decent solution. Hope it helps someone.

JJD
  • 50,076
  • 60
  • 203
  • 339
unmultimedio
  • 1,224
  • 2
  • 13
  • 39
1

If you need to wait for possible user interactions to start, use OnMapLoadedCallbackas described in the previous answers. But, if all you need is to provide a default location for the map, there is no need for any of the solutions outlined in those answers. Both MapFragmentand MapView can accept a GoogleMapOptions at start that can provide the default location all right. The only trick is not to include them directly in your layout because the system will call them without the options then but to initialize them dynamically.

Use this in your layout:

<FrameLayout
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

and replace the fragment in your onCreateView():

GoogleMapOptions options = new GoogleMapOptions();
options.camera(new CameraPosition.Builder().target(location).zoom(15).build());
// other options calls if required

SupportMapFragment fragment = (SupportMapFragment) getFragmentManager().findFragmentById(R.id.map);
if (fragment == null) {
  FragmentTransaction transaction = getFragmentManager().beginTransaction();
  fragment = SupportMapFragment.newInstance(options);
  transaction.replace(R.id.map, fragment).commit();
  getFragmentManager().executePendingTransactions();
}
if (fragment != null)
  GoogleMap map = fragment.getMap();

Besides being faster to start, there will be no world map shown first and a camera move second. The map will start with the specified location directly.

Gábor
  • 9,466
  • 3
  • 65
  • 79
0

Another approach would something like (Assuming your topmost view is a FrameLayout named rootContainer, even though it will work as long as you always choose your topmost container no matter which type or name it has):

((FrameLayout)findViewById(R.id.rootContainer)).getViewTreeObserver()
    .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        public void onGlobalLayout() {
            layoutDone = true;
        }
    });

Modifying your camera functions to only work if layoutDone is true will solve all your problems without having to add extra functions or wire up logic to the layoutListener handler.

JJD
  • 50,076
  • 60
  • 203
  • 339
Machinarius
  • 3,637
  • 3
  • 30
  • 53
0

I found this to work and be more simple than the other solutions:

private void moveMapToBounds(final CameraUpdate update) {
    try {
        if (movedMap) {
            // Move map smoothly from the current position.
            map.animateCamera(update);
        } else {
            // Move the map immediately to the starting position.
            map.moveCamera(update);
            movedMap = true;
        }
    } catch (IllegalStateException e) {
        // Map may not be laid out yet.
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                moveMapToBounds(update);
            }
        });
    }
}

This tries the call again after layout has run. Could probably include a safety to avoid an infinite loop.

John Patterson
  • 497
  • 3
  • 11
0

Why not just use something like this:

CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), padding);
try {
    map.moveCamera(cameraUpdate);
} catch (Exception e) {
    int width = getResources().getDisplayMetrics().widthPixels;
    int height = getResources().getDisplayMetrics().heightPixels;
    cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, padding);
    map.moveCamera(cameraUpdate);
}
  • Why `newLatLngBounds(builder.build(), padding)` is not inside try-catch? Does this exception occur in `moveCamera()`? – CoolMind Mar 04 '19 at 09:33
0

As stated in OnCameraChangeListener() is deprecated, setOnCameraChangeListener is now deprecated. So you should replace it with one of three mtehods:

  • GoogleMap.OnCameraMoveStartedListener
  • GoogleMap.OnCameraMoveListener
  • GoogleMap.OnCameraIdleListener

In my case, I used OnCameraIdleListener and inside I removed it because it was invoked again and again on any movement.

googleMap.setOnCameraIdleListener {
    googleMap.setOnCameraIdleListener(null) // It removes the listener.
    googleMap.moveCamera(track)
    googleMap.cameraPosition
    clusterManager!!.cluster()
    // Add another listener to make ClusterManager correctly zoom clusters and markers.
    googleMap.setOnCameraIdleListener(clusterManager)
}

UPDATE

I removed googleMap.setOnCameraIdleListener in my project, because it wasn't called sometimes when a map was shown, but retained googleMap.setOnCameraIdleListener(clusterManager).

Nensi Kasundra
  • 1,980
  • 6
  • 21
  • 34
CoolMind
  • 26,736
  • 15
  • 188
  • 224
0

There is a helper class in the Google Maps repo that you can leverage - it waits for both the layout and map to be ready before notifying a callback with the GoogleMap:

The original source is here:

https://github.com/googlemaps/android-samples/blob/7ee737b8fd6d39c77f8b3716ba948e1ce3730e61/ApiDemos/java/app/src/main/java/com/example/mapdemo/OnMapAndViewReadyListener.java

There is a Kotlin implementation too:

https://github.com/googlemaps/android-samples/blob/master/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt

public class OnMapAndViewReadyListener implements OnGlobalLayoutListener, OnMapReadyCallback {

/** A listener that needs to wait for both the GoogleMap and the View to be initialized. */
public interface OnGlobalLayoutAndMapReadyListener {
    void onMapReady(GoogleMap googleMap);
}

private final SupportMapFragment mapFragment;
private final View mapView;
private final OnGlobalLayoutAndMapReadyListener devCallback;

private boolean isViewReady;
private boolean isMapReady;
private GoogleMap googleMap;

public OnMapAndViewReadyListener(
        SupportMapFragment mapFragment, OnGlobalLayoutAndMapReadyListener devCallback) {
    this.mapFragment = mapFragment;
    mapView = mapFragment.getView();
    this.devCallback = devCallback;
    isViewReady = false;
    isMapReady = false;
    googleMap = null;

    registerListeners();
}

private void registerListeners() {
    // View layout.
    if ((mapView.getWidth() != 0) && (mapView.getHeight() != 0)) {
        // View has already completed layout.
        isViewReady = true;
    } else {
        // Map has not undergone layout, register a View observer.
        mapView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    // GoogleMap. Note if the GoogleMap is already ready it will still fire the callback later.
    mapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
    // NOTE: The GoogleMap API specifies the listener is removed just prior to invocation.
    this.googleMap = googleMap;
    isMapReady = true;
    fireCallbackIfReady();
}

@SuppressWarnings("deprecation")  // We use the new method when supported
@SuppressLint("NewApi")  // We check which build version we are using.
@Override
public void onGlobalLayout() {
    // Remove our listener.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    } else {
        mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    isViewReady = true;
    fireCallbackIfReady();
}

private void fireCallbackIfReady() {
    if (isViewReady && isMapReady) {
        devCallback.onMapReady(googleMap);
    }
}
}
dazza5000
  • 7,075
  • 9
  • 44
  • 89
0

I had the same error trying to call mMap.animateCamera. I solved it by calling setOnMapLoadedCallback callback in my onMapReady function as shown below

public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

   // other codes go there

    mMap.setOnMapLoadedCallback(() -> {
            //Your code where exception occurs goes here...
            
        });
}

This should work fine.

Paul Kumchi
  • 211
  • 4
  • 7