Accessing GoogleMap from outside the UI thread in Android - android

I'm using Google Map Android API v2 for my android application. There I have initialize GoogleMap as follows inside onCreate
SupportMapFragment fm = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
mGoogleMap = fm.getMap();
In my application, I need to do some background tasks and I'm using AsybcTask for that. While progress through the background tasks I want to update the map. Please refer the following sample.
#Override
protected Void doInBackground(Void...voids) {
while(Condition)
{
mGoogleMap.clear();
mGoogleMap.addMarker(marker);
Thread.sleep(5000);
}
}
I know that I can't directly access UI components inside the doInBackground method. Common UI components like buttons, edittexts, textviews can be accessed by parsing the context of the application to the AsyncTask class. But I can't figure out a way to access GoogleMap from the doInBackground method. Could you please give me a solution.

if your AsyncTask is a part of the Activity you can simply use runOnUiThread method, like this:
#Override
protected Void doInBackground(Void...voids) {
while(Condition)
{
runOnUiThread(new Runnable() {
#Override
public void run() {
mGoogleMap.clear();
mGoogleMap.addMarker(marker);
Thread.sleep(5000);
}
});
}
}

Related

Android GoogleMaps slow onMapClick response

I set a click listener (GoogleMap.OnMapClickListener) for GoogleMap object in the onMapReady() method like this.
#Override
public void onMapReady(GoogleMap map) {
addClickListener(map);
}
Here is the addClickListener():
private void addClickListener(GoogleMap map) {
map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
#Override
public void onMapClick(LatLng point) {
Log.d("ON_MAP_CLICK", "1. OnMapClick called");
handleOnMapClick(point);
}
});
}
Since I set the map ready in the onMapReady method it is logical that I also add the click listener there.
The problem is that the delay for clicking on the map and the code to register that click is around 1 second while is should be instant.
Problem: The problem is not the time it takes to call addClickListener(GoogleMap) but the time that onMapClick() registers a click on the map (around 1 second).
Why is it so slow?
I am using GoogleMap V2
Thanks!
It is obvious that addClickListener method is called after 1 second because onMapReady call back will called only after intializing the map. So it take some delay initially for calling your addClickListener method.
So you will observe this delay only for the first time not all the time and it is working as like it is expected, No changes required in your code
onMapReady will call after the map is initialized, i had a project that took a while to load the map making all the functions "slow".
You can try to load the map earlier to prevent that by adding this to the Splashscreen or Activity.
new Thread(new Runnable() {
#Override
public void run() {
try {
MapView mv = new MapView(getApplicationContext());
mv.onCreate(null);
mv.onPause();
mv.onDestroy();
}catch (Exception ignored){
}
}
}).start();

How to pause at an Android lifecycle stage until callback is called?

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???
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
If your map object is null when onResume() is called, that means the onMapReady callback will be called further.
If your map object is set when 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();
}
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.
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
}

How to detect moveCamera is finished on the Google map service?

I want to take a snapshot when moveCamera is finished.
I used both onCamerachange() and onMapLoaded() functions to Googlemap object,
But this callback handler's timing is not fit on my intention.
I want to move camera to what I want, and when loading map is finished, want to take a screnshot.
Who do you guys know about how can I do?
Thank you.
====================================================
+MYSOLUTION
Actually, I want to implement tilesloaded() call back function that is supported on the google map API of the javascript.
But, as a result, there is no function which is completely accomplished that function.
OnMapLoaded() callback function is just invoked once when the map is loaded first.
OnCameraChanged() or OnCancallableCallback is invoked before all the tiles are loaded.
So I implemented like below source code
...
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 10), this);
...
#Override
public void onFinish() {
mapFragment.getMapAsync(this);
}
..
#Override
public void onMapReady(GoogleMap googleMap) {
// TODO Auto-generated method stub
mMap = googleMap;// Add a marker in Sydney and move the camera
mMap.setOnMapLoadedCallback(this);
}
Now, when the camera is moved, onMapLoaded() is always invoked.
Thank you everybody.
You can implement this:
mMap.animateCamera(CameraUpdateFactory.zoomTo(16), 3000, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
//Here you can take the snapshot or whatever you want
}
#Override
public void onCancel() {
}
});
The most detailed form of this method, GoogleMap.animateCamera(cameraUpdate, duration, callback), offers three arguments:
cameraUpdate
The CameraUpdate describing where to move the camera.
callback
An object that implements GoogleMap.CancellableCallback. This generalized interface for handling tasks defines two methods onCancel() and onFinished(). For animation, the methods are called in the following circumstances:
onFinish()
Invoked if the animation goes to completion without interruption.
onCancel()
Invoked if the animation is interrupted by calling stopAnimation() or starting a new camera movement.
Alternatively, this can also occur if you call GoogleMap.stopAnimation().
Source: Google Maps Android API Guides
So you are definitely in the right area by using the onCameraChange() and onMapLoaded() callback. The best way to handle this would be to handle your screenshot in the onMapLoaded() function.
Also, remember to use the newest libraries, and the support libraries when instantiating your map fragment. This is very important.
So:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...All of the other important things you have here
//Instantiate the map object using the support library
map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();
map.getMapAsync(this);
//Add the callback to your map object
map.setOnMapLoadedCallback(this);
}
#Override
public void onMapLoaded(){
//Perform screenshot here
}
EDIT
Here is code for a handler, complete with comments.
#Override
public void onCameraChange(){
//Constant for our time. I would put this below your class declaration
private static final int DELAY_TIME_IN_MILLI = 3000;
//Here is the handler for waiting
mMap.postDelayed(new Runnable() {
//This function runs after the time allotted below
#Override
public void run() {
//Call your screenshot function here
takeScreenShot();
}
},DELAY_TIME_IN_MILLI); //Our delay
}

how to return info from UI elements into thread

I've created an app with a GoogleMap.
I want to do some maths and update the GoogleMap's markers inside a thread that runs all the time.
My thread needs to know the current googleMap's camera zoom and also the LatLngBounds to update the markers.
If I do that it works perfectly:
class updateMap implements Runnable{
private double currentZoom;
private LatLngBounds screenRegion;
#Override
public void run(){
while(!threadStop){//threadStop is a boolean to stop threads when onDestroy() is called
screenRegion = googleMap.getProjection().getVisibleRegion().latLngBounds;//fetches the screen's visible region
currentZoom = googleMap.getCameraPosition().zoom;
if(googleMap != null){//if my googleMap is fine
if(currentZoom >= 13){ //if the Camera's zoom is greater or equal than 13
//do some maths...
//updates the markers on the GoogleMap
}
}
}
}
}
but I know it's not recommended to interact with the UI thread directly within the thread so I've put the few lines of code that interact with the UI thread inside the method runOnUiThread() as followed but it doesn't work. My app freezes and nothing happens, ARN doesn't even show up !
class updateMap implements Runnable{
private double currentZoom;
private LatLngBounds screenRegion;
boolean map = false;
#Override
public void run(){
while(!threadStop){//threadStop is a boolean to stop threads when onDestroy() is called
runOnUiThread(new Runnable() {//Here I execute this piece of code on the UI thread to get the variables
#Override
public void run() {
if(googleMap != null){
map = true;
screenRegion = googleMap.getProjection().getVisibleRegion().latLngBounds;//fetches the screen's visible region
currentZoom = googleMap.getCameraPosition().zoom;
}
}
});
if(map){//if my googleMap is fine
if(currentZoom >= 13){ //if the Camera's zoom is greater or equal than 13
//do some maths...
runOnUiThread(new Runnable(){
#Override
public void run(){
//updates the markers on the GoogleMap
}
});
}
}
}
}
}
If anyone has any idea ? Maybe I don't respect the syntax or maybe I shouldn't use the runOnUiThread() method twice or something...
Thanks in advance.
I think using UI thread to get data is wrong in your case. Do it before separate thread start and pass as parameters.

Android : Make application wait till the current location is found

I'm trying to make my app wait till the current location is found. I've tried few different ways using Threads and all have failed really. I was using wait() and notify() but application just hung and never found the current location.
I amen't using google map api as it is not part of the application. Does anyone have any ideas how to do this or examples.
EDIT : The Thread I was using did not start till the user pressed a button then within onLocationChanged other data is processed e.g. adding the new location to ArrayList, Calculate the distance between the current and last Location as well as the Time taken to get to the new location
You could try starting an AsyncTask in onCreateto get the location. Your default onCreate layout could be a "loading" page, then when your AsyncTask completes successfully with the location it draws your "real" UI.
So if I understand what you want to do correctly, then I would avoid making another thread in onClick(). Instead, onClick() should just request a location, display a progress dialog, and return. Since the work you want to do happens after you receive the new location, I would start an AsyncTask there. Then you finally remove the dialog box (removing it returns control to the user) when the AsyncTask finishes.
Code usually helps, so, I would put this in onCreate() or wherever:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.refresh();
}
});
And put this in your LocationListener:
public void refresh() {
myLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
myDialog = new ProgressDialog(myContext);
myDialog.setIndeterminate(true);
myDialog.show();
}
#Override
public void onLocationChanged(Location location) {
// now do work with your location,
// which your probably want to do in a different thread
new MyAsyncTask().execute(new Location[] { location });
}
And then you need an AsyncTask, which may look like this:
class MyAsyncTask extends AsyncTask<Location, Void, Void> {
#Override
protected Void doInBackground(Location... location) {
// start doing your distance/directions/etc work here
return null;
}
#Override
protected void onPostExecute(Void v) {
// this gets called automatically when you're done,
// so release the dialog box
myDialog.dismiss();
myDialog = null;
}
}

Categories

Resources