Reputation: 1
I am trying to use Google Maps API in my Android app. I need to call getMapAsync() to retrieve the map object asynchronously and I need to get the GoogleMap object instance before my fragment calls onResume(), because otherwise the GoogleMap object will still be null and it will throw an exception.
Therefore, I want the Activity that holds the fragment to pause at onResume() until onMapReadyCallback is invoked, so that I am guaranteed that the GoogleMap object is created and I could proceed in the Activity lifecycle. However, if I use any locks or barrier synchronizers in the Activity's thread (main thread), the thread will block and will not receive any callbacks and thus will wait forever.
I tried to use an object to implement the onMapReadyCallback and put it in another thread, but according to the documentation, I must have the callback in the main thread.
So how can I make the main thread wait until the callback has arrived and prevent it from running down its lifecycle methods???
Upvotes: 0
Views: 1511
Reputation: 229
You have two methods to resolve your issue. One crappy, and one better.
The crappy way
If this can helps for tests purposes for instance, this can works.
Call the Handler#postDelayed
method in your onResume()
method when your map object is null. Here's an example.
@Override
public void onResume(){
super.onResume();
doMapStuff();
}
public void doMapStuff()
{
if (mMap == null) {
new Handler().postDelayed(new Runnable() {
public void run() {
doMapStuff();
}
}, 250);
return;
}
// do your stuff on the map here.
}
public void onMapReady(GoogleMap map) {
mMap = map;
doMapStuff();
}
As the handler is created on the UI thread, the delayed runnable will also be executed on the UI thread, so you will be able to make your map stuff. BUT This is a crappy way to do this. It can works for tests, but avoid using this way!
The good way
onResume()
is called, that means the onMapReady
callback will be called further.onResume()
is called, that means the onMapReady
callback was already called, so it will not be called again.Basically, you need to "duplicate" your calls between onResume()
and onMapReady()
methods.
@Override
public void onResume(){
super.onResume();
doMapStuff();
}
public void doMapStuff()
{
if (mMap == null)
return;
// do your stuff on the map here.
}
public void onMapReady(GoogleMap map) {
mMap = map;
doMapStuff();
}
Upvotes: 1
Reputation: 1421
Do a check to make sure that onMapReady has completed. Would this work for you:
MapFragment mMapFragment;
@Override
public View onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
public void onMapReady(GoogleMap map){
mMap = map;
}
then in onResume do a check:
@Override
public void onResume(){
super.onResume();
if(mMap == null){
mMapFragment.getMapAsync(this);
}
//rest of code
}
Upvotes: 0
Reputation: 317750
You can't pause or delay the activity lifecycle without causing problems, as you have seen.
If all the data you need is not available at the time of an activity lifecycle method, you can delay the visibility of the UI and postpone other work that needs to be done in the Activity. Show some sort of spinner or progress bar until all the data and connections are available, then update the views in response to everything becoming available.
Upvotes: 0