AsyncTask cancel method throws handler error

366 Views Asked by At

I have a simple activity in which I start an AsyncTask with a button and stop it with another.

In AsyncTask I use a LocationListner to get updates from the GPS sensor and calculate the distance between obtained Locations.

When I press the start button, new thread is created and the code works fine. In the debug perspective I can see that GPS updates are caught regularly and my variables are updated accordingly.

However, the problem starts when I press the cancel button. In the documentation (http://developer.android.com/reference/android/os/AsyncTask.html) I have read that cancel() method should invoke onCancelled() when doInBackground code finishes. For that reason I have put:

if (isCancelled()){
    locManager.removeUpdates(locListener);
}

all over the doInBackground method, hoping that it will get me out of it. Unfortunately, I don't even get to that part. Once when I press the end button I get the following error:

05-22 11:10:05.043: E/Handler(6353): java.lang.NullPointerException
05-22 11:10:05.043: E/Handler(6353):    at com.pavle.taximetar.TaximetarActivity$2.onClick(TaximetarActivity.java:43)
05-22 11:10:05.043: E/Handler(6353):    at android.view.View.performClick(View.java:3538)
05-22 11:10:05.043: E/Handler(6353):    at android.view.View$PerformClick.run(View.java:14330)
05-22 11:10:05.043: E/Handler(6353):    at android.os.Handler.handleCallback(Handler.java:607)
05-22 11:10:05.043: E/Handler(6353):    at android.os.Handler.dispatchMessage(Handler.java:92)
05-22 11:10:05.043: E/Handler(6353):    at android.os.Looper.loop(Looper.java:154)
05-22 11:10:05.043: E/Handler(6353):    at android.app.ActivityThread.main(ActivityThread.java:4974)
05-22 11:10:05.043: E/Handler(6353):    at java.lang.reflect.Method.invokeNative(Native Method)
05-22 11:10:05.043: E/Handler(6353):    at java.lang.reflect.Method.invoke(Method.java:511)
05-22 11:10:05.043: E/Handler(6353):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
05-22 11:10:05.043: E/Handler(6353):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
05-22 11:10:05.043: E/Handler(6353):    at dalvik.system.NativeStart.main(Native Method)

Any idea, what's behind this error. Here is the full code. (Build target is Android 4.0.3). The code is tested on HTC One V.

public class TaximetarActivity extends Activity {

DistanceCalculator distanceCalculator;
public ArrayList<Drive> listOfRides = new ArrayList<Drive>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final Button startRide = (Button) findViewById(R.id.button1);
    final Button endRide = (Button) findViewById(R.id.button2);

    startRide.setOnClickListener(new OnClickListener() {        
        public void onClick(View v) {
            distanceCalculator = new DistanceCalculator();
            distanceCalculator.execute(getApplicationContext());
        }   
    });

    endRide.setOnClickListener(new OnClickListener() {  
        public void onClick(View v) {
            distanceCalculator.cancel(true);
        }
    });
}
}

public class DistanceCalculator extends AsyncTask<Context, Void, Void> {

Drive currentRide = new Drive();
Float distanceOfaRide = (float) 0;
LocationManager locManager;
LocationListener locListener;
Context mCtx;
boolean startPointEntered = false;

@Override
protected Void doInBackground(Context... params) {

    mCtx = params[0];


    Looper.prepare();

    if (isCancelled()){
        locManager.removeUpdates(locListener);
    }

    locManager = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
    locListener = new LocationListener() {

        public void onStatusChanged(String provider, int status, Bundle extras) {
            if (isCancelled()){
                locManager.removeUpdates(locListener);
            }
        }

        public void onProviderEnabled(String provider) {
            if (isCancelled()){
                locManager.removeUpdates(locListener);
            }
        }

        public void onProviderDisabled(String provider) {
            if (isCancelled()){
                locManager.removeUpdates(locListener);
            }               
        }

        public void onLocationChanged(Location location) {
            if (DistanceCalculator.this.isCancelled()){
                locManager.removeUpdates(locListener);
            }
            calculateDistance(location);
        }
    };

    locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 0, locListener);
    Looper.loop();
    return null;
}

@Override
protected void onCancelled() {
    locManager.removeUpdates(locListener);
    Intent intent = new Intent(mCtx, DisplayRide.class);
    intent.putExtra("Distance", distanceOfaRide);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    mCtx.startActivity(intent);
}

private void calculateDistance(Location location){
    if (startPointEntered){
        Location lastPoint = currentRide.drivePoints.get(currentRide.drivePoints.size()-1);
        currentRide.addNewPoint(location);
        distanceOfaRide = distanceOfaRide + location.distanceTo(lastPoint);
    }
    else{
        currentRide.setStartPoint(location);
        startPointEntered = true;
    }
}
}

public class Drive {

    Location startPoint;
    Location endPoint;
    ArrayList<Location> drivePoints = new ArrayList<Location>();

    public Drive() {
        // TODO Auto-generated constructor stub
    }

    public void setStartPoint(Location location){
            startPoint = new Location(location);
        drivePoints.add(0, startPoint);
    }
    public void setEndPoint(Location location){
        endPoint = new Location(location);  
    }
    public void addNewPoint(Location location){
        drivePoints.add(location);
    }
    public Location getStartPoint(){
        return startPoint;
    }
}
2

There are 2 best solutions below

0
On BEST ANSWER

Problem was in this part of the code:

endRide.setOnClickListener(new OnClickListener() {  
    public void onClick(View v) {
        distanceCalculator.cancel(true);
    }
});

Here, it is required to send a message to a handler which is created in the Async task. In response to that message, the message handler function should cancel the task.

2
On

You're performing actions on locManager before you're assigning it.

if (isCancelled()){
    locManager.removeUpdates(locListener);
}

locManager = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);

Versus...

locManager = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
if (isCancelled()){
    locManager.removeUpdates(locListener);
}