6

The final thing I am doing is: I have the google map in my application with som buttons and extra stuffs. but I also have to show the updated location of a marker all the time inside my map (I will get the data from external device not gps sensor of android phone). So I thought using thread first but then changes to AsyncTask as you see in my code and tried to use addMarker method there but every time I run it map just stopps working. I want to just show one marker on map now but now successful! The GoogleMap map cannot be reachable via UI thread and inside AsyncTask why? anybody encountered this? what should i do to solve this... plz help

 package com.example.mapsversion2;

    //All imports

    public class MainActivity extends Activity  {
    static final LatLng exmpoint = new LatLng(59.331438,18.064957);

    public static GoogleMap map;

    Button button;
    Button waypointButton;
    Button destinationButton;
    Marker wayPoint;
    TextView positiontext;
    int countclick= 3;
    public static DataModel autoboxdata= new DataModel();
    public static Bitmap arrowBitmap;
    Marker currentPlace;
    MapFragment mapF;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
     mapF = ((MapFragment) getFragmentManager().findFragmentById(R.id.map));
     map= mapF.getMap();
     map.setMapType(GoogleMap.MAP_TYPE_HYBRID);

    //  positiontext = (TextView) findViewById(R.id.position);

    // Move the camera instantly to hamburg with a zoom of 15.
    map.moveCamera(CameraUpdateFactory.newLatLngZoom(exmpoint,15));

    // Zoom in, animating the camera.
    map.animateCamera(CameraUpdateFactory.zoomTo(6), 2000, null);

    addListenerOnButton();
    map.setOnMarkerDragListener(this);

    new PositionUpdate1().execute(); 
  }
    @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
  }

  public void addListenerOnButton() {

        destinationButton= (Button) findViewById(R.id.destinationB);

        destinationButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                //do something
            }
        });     
    }

  }

    public class PositionUpdate1  extends AsyncTask<Void, Void, Void>{


    @Override
    protected Void doInBackground(Void...arg0) {
        // TODO Auto-generated method stub


        map.addMarker(new MarkerOptions()
            .position(hereIam) );

        System.out.println("hej");
        return null;
    } 
  }
    } 

Here is the LogCat:

02-17 18:57:17.805: E/AndroidRuntime(9905): FATAL EXCEPTION: main
02-17 18:57:17.805: E/AndroidRuntime(9905): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.mapsv2/com.example.mapsv2.MainActivity}: java.lang.NullPointerException
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2100)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2125)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.ActivityThread.access$600(ActivityThread.java:140)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1227)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.os.Looper.loop(Looper.java:137)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.ActivityThread.main(ActivityThread.java:4898)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at java.lang.reflect.Method.invokeNative(Native Method)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at java.lang.reflect.Method.invoke(Method.java:511)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at dalvik.system.NativeStart.main(Native Method)
02-17 18:57:17.805: E/AndroidRuntime(9905): Caused by: java.lang.NullPointerException
02-17 18:57:17.805: E/AndroidRuntime(9905):     at com.example.mapsv2.MainActivity.onCreate(MainActivity.java:140)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.Activity.performCreate(Activity.java:5206)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1083)
02-17 18:57:17.805: E/AndroidRuntime(9905):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2064)
02-17 18:57:17.805: E/AndroidRuntime(9905):     ... 11 more
02-17 18:57:17.935: E/android.os.Debug(2287): !@Dumpstate > dumpstate -k -t -z -d -o /data/log/dumpstate_app_error
Vivere_FlowCoder
  • 277
  • 1
  • 4
  • 14

3 Answers3

17

You are trying to add a marker to the map in the background thread, but you can't update UI in Android from thread other than UI thread. So, move your map.addMarker call to the onPostExecute method, which is called in UI thread after background operation is complete:

public class PositionUpdate1  extends AsyncTask<Void, Void, Location> {

    @Override
    protected Location doInBackground(Void...arg0) {
        // get your location...
        return hereIam;
    }

    @Override
    protected void onPostExecute(Location loc) {
        map.addMarker(new MarkerOptions().position(loc));
    }
}

If you want to update the marker continuously, the easiest way is to use a Handler:

public class MainActivity extends Activity {

    final int MARKER_UPDATE_INTERVAL = 2000; /* milliseconds */
    Handler handler = new Handler();

    GoogleMap map;
    Marker marker;
    Location location;

    Runnable updateMarker = new Runnable() {
        @Override
        public void run() {
            marker.remove();
            marker = map.addMarker(new MarkerOptions().position(location));

            handler.postDelayed(this, MARKER_UPDATE_INTERVAL);
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        map = ...; // get map
        location = ...; // get location
        marker = map.addMarker(new MarkerOptions().position(location));

        handler.postDelayed(updateMarker, MARKER_UPDATE_INTERVAL);
    }

    @Override
    protected void onDestroy() {
        handler.removeCallbacks(updateMarker);

        super.onDestroy();
    }
}
whyleee
  • 4,019
  • 1
  • 31
  • 33
  • I did this. still app stops working. with the same Logcat errors – Vivere_FlowCoder Feb 17 '13 at 18:24
  • Ok, you also have a `NullPointerException` at 140 row in `MainActivity`. Are you sure your `map` is not null? (`mapF.getMap()` call will return `null` if Google Play Services are not installed on the device, so you should handle such case) – whyleee Feb 17 '13 at 18:29
  • when i add the marker in main Thread inside increate everything works. and I see the marker.. and yes I installed Google play Services – Vivere_FlowCoder Feb 17 '13 at 19:10
  • Ok, can you check what is `null` when you get an exception (in debugger)? – whyleee Feb 17 '13 at 19:16
  • Emm.. Android debugging is another topic, try to find tutorials in the Internet (or try to read this: http://mobile.tutsplus.com/tutorials/android/android-app-debugging/) – whyleee Feb 17 '13 at 21:07
  • Do you know how should i make this marker to get updated every for example second? because i will receive new position contiguously.. – Vivere_FlowCoder Feb 18 '13 at 00:07
  • I think I'd use `Handler` for that, look here: http://stackoverflow.com/questions/6207362/how-to-run-an-async-task-afor-every-x-mins-in-android#answer-6207519 (code in the `run` method is called in UI thread, so you can update the map there) – whyleee Feb 18 '13 at 00:18
  • Or can I put the execution of AsyncTask inside timing handler?! – Vivere_FlowCoder Feb 18 '13 at 00:25
  • Probably you can. But maybe it is better to just replace `AsyncTask` by a handler, if background operation is short (for example, only to add a marker, without any other IO or network operations)... – whyleee Feb 18 '13 at 00:33
  • but can handler access to UI thread? I am asking cause I could not addmarker to map by using Thread – Vivere_FlowCoder Feb 18 '13 at 00:35
  • Handler's `run` method called in the UI thread, so use it instead of ordinary Java `Thread`. – whyleee Feb 18 '13 at 00:38
  • I will try it tmrw and will put the result – Vivere_FlowCoder Feb 18 '13 at 00:41
  • I used handler.postdelayed it is working. and i am puting and removing a marker after some seconds. but the thing is map is becomming slow. when i pan around it is not real time or other buttons are not responding quick – Vivere_FlowCoder Feb 18 '13 at 11:57
  • It is because you doing a lot of things in UI thread (like IO or network operations) or calling `postDelayed` too often. Try reduce the interval and/or move all slow code (calculations) to the background thread (using AsyncTask for example). Operation to only add several markers to the map should not slow down your map experience itself. – whyleee Feb 18 '13 at 12:54
  • i have only 3 buttons on map besides showing the one marker continousely. one of buttons adds one marker on map which is draggable. other button adds three markers. and 3rd button doing nothing for now. and i dont have network operations yet.. – Vivere_FlowCoder Feb 18 '13 at 13:58
  • Ok, but something is blocking your UI, if your map is slow. Maybe it's not related to your code in the handler. – whyleee Feb 18 '13 at 14:14
  • mmm but when i remove handler it is becomming really fast and smooth – Vivere_FlowCoder Feb 18 '13 at 14:55
  • final Handler handler = new Handler(); Timer timer = new Timer(); TimerTask doAsynchronousTask = new TimerTask() { @Override public void run() { handler.post(new Runnable() { public void run() { Marker truckHere= map.addMarker(new MarkerOptions() .position(new LatLng(40.417325, 40.417325))); sleep 2sec truckHere.remove(); sleep2 sec } }); } }; timer.schedule(doAsynchronousTask, 0, 6000); – Vivere_FlowCoder Feb 18 '13 at 15:03
  • oh i tried to put my handler thing. it looks not so good. but this is the whole thing im doing in handler. put a marker, wait for 2 sec, remove that marker, wait again 2 sec. and whole process executes every 6 sec – Vivere_FlowCoder Feb 18 '13 at 15:04
  • Calling some sort of a `Thread.sleep` in the handler's `run` method will block your UI thread, that's not correct solution. I'll update the answer with example of correct handler usage (the code in comments looking ugly :)) – whyleee Feb 18 '13 at 15:20
  • 3
    I voted up this because you gave an impressive help. Faith in humanity restored :) . – Surasin Tancharoen Feb 18 '13 at 20:54
  • thanks whylee for the time you put. I tried it but code crashes cause marker.remove is the first line of run(). i put it after handler.postDelayed(this, MARKER_UPDATE_INTERVAL); now i dont have anything on my map – Vivere_FlowCoder Feb 18 '13 at 22:37
  • and why we have two handler.postDelayed? cant we just put the whole runnable in oncreate? – Vivere_FlowCoder Feb 18 '13 at 22:38
  • Sorry, I skipped some details in the example: your code crashes because you need to create a marker first in `onCreate` method. See updated `onCreate` and `updateMarker` methods in the answer. – whyleee Feb 19 '13 at 08:00
  • We have two `postDelayed` calls, because handler will run the task only once after specified time interval. To make a loop, we need to call `postDelayed` again after each task run. – whyleee Feb 19 '13 at 08:05
3

You can do something like this. Load the data you need in doInBackground and draw the markers in onProgressUpdate. To call onProgressUpdate just do a publishProgress() from doInBackground. That way you can update the UI from within the ASyncTask. Works for me.

public class CDrawHotspotsAsync extends AsyncTask<Void, CHotspot, Boolean>
{
private final Context mCntxt;
private final GoogleMap mGMap;

// ---------------------------------------------------------

public CDrawHotspotsAsync(Context cntxt, GoogleMap gmap)
{
    this.mCntxt = cntxt;
    this.mGMap = gmap;
}

// ---------------------------------------------------------

private void drawMarker(GoogleMap gmap, CHotspot hotspot)
{
    // Set marker options
    MarkerOptions mo = new MarkerOptions();
    mo.position(hotspot.getPosition());
    mo.title(hotspot.getName());
    String descr = hotspot.getAddress() + ", " + hotspot.getPostal() + ", " + hotspot.getCity();
    mo.snippet(descr);
    mo.icon(BitmapDescriptorFactory.fromResource(R.drawable.hotspot_marker));

    // Add marker to map
    gmap.addMarker(mo);
}

// ---------------------------------------------------------

@Override
protected Boolean doInBackground(Void... params)
{
    // Get hotspots from database
    CHotspotList hotspots = new CHotspotList(this.mCntxt);
    ArrayList<CHotspot> arrHotspots = hotspots.getHotspotList();

    for (CHotspot hotspot : arrHotspots)
    {
        // Publish progress
        publishProgress(hotspot);
    }

    // Always return true
    return true;
}

// ---------------------------------------------------------

@Override
protected void onProgressUpdate(CHotspot... hotspot)
{
    // Draw the marker
    drawMarker(this.mGMap, hotspot[0]);
}
}
1

If your async task is loading more than one marker you can do the following:

private class AddMarker implements Runnable {

    private GoogleMap map;
    private MarkerOptions options;

    public AddMarker(GoogleMap map, MarkerOptions options) {
        this.map = map;
        this.options = options;
    }

    @Override
    public void run()
    {
        map.addMarker(options);
    }
}
Runnable addMarker = new AddMarker(map, markerOptions);
activity.runOnUiThread(addMarker);

And, of course, you should construct the async task with the activity object.

Marshall
  • 87
  • 2
  • 8