Alon Levy Alon Levy - 3 months ago 20
Android Question

AsyncTask isCancelled() returns true but doInBackground doesn't end

I have an AsyncTask which record audio. The problem is once I call

cancel(true)
from the UI thread,
isCancelled()
returns
true
(like it's supposed to) but the
doInBackground
function keeps running and if I run
getStatus()
on the AsyncTask it returns RUNNING and neither of the
onCancelled
methods are called nore does
onPostExecute
...

Since all the
doInBackground
code is placed inside
while(!isCancelled())
, the method should end once the task is cancelled...

The while(!isCancelled()) loop ends once I call AsyncTask.cancel(). I think the problem is that
return null
isn't being called and that's why
doInBackground
doesn't end... I may be wrong though...

code:

package Classes;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.util.Log;

import com.appetizers.app.trytabs.Main2Activity;

/**
* Created by Alon on 09-Aug-16.
*/
public class RecordingTask2 extends AsyncTask<Void,Void,Void> {

AudioRecord record;
Context context;
int bufferSize;
int numSample=0;
private static int[] mSampleRates = new int[] { 8000, 11025, 22050, 44100 };
short[] audioBuffer;

public RecordingTask2(Context context) {
this.context = context;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d("RECTASK", "onPre");
record = findAudioRecord();
record.startRecording();
}

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

audioBuffer = new short[bufferSize/2];

Log.d("RECTASK","before while");
while(!isCancelled())
{
//Log.d("RECTASK","isCancelled = "+isCancelled());
int numberOfShort = record.read(audioBuffer, 0, audioBuffer.length);
numSample +=numberOfShort;
}
Log.d("RECTASK", "after while\nisCancelled = "+isCancelled());

return null;
}

@Override
protected void onCancelled() {
super.onCancelled();
Log.d("RECTASK", "reached onCancel()");
end();
}

@Override
protected void onCancelled(Void aVoid) {
super.onCancelled(aVoid);
Log.d("RECTASK", "reached onCancelled(Void)");
end();
}

@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Log.d("RECTASK", "reached onPost");
end();
}

private void end()
{
Log.d("RECTASK","reached end function");
record.stop();
record.release();
((Main2Activity)context).setNumOfSamples(numSample);
((Main2Activity)context).setSamples(audioBuffer);
}

public AudioRecord findAudioRecord() {
for (int rate : mSampleRates) {
for (short audioFormat : new short[] { AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT }) {
for (short channelConfig : new short[] { AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO }) {
try {
Log.d("FINDAUDIO", "Attempting rate " + rate + "Hz, bits: " + audioFormat + ", channel: "
+ channelConfig);
bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);

if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
// check if we can instantiate and have a success
AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, rate, channelConfig, audioFormat, bufferSize);

Log.d("FINDAUDIO", "AudioRecorder state = "+recorder.getState());

if (recorder.getState() == AudioRecord.STATE_INITIALIZED)
return recorder;
}
} catch (Exception e) {
Log.e("FINDAUDIO", rate + "Exception, keep trying.",e);
}
}
}
}
return null;
}

}


Here's the part of the code in my MainActivity class where I call and cancel the AsyncTask

recordSlideBar.setDownRun(new Runnable() {
@Override
public void run() {
Log.d("MAINACT","onDown");
if(recTask==null || recTask.getStatus()!= AsyncTask.Status.RUNNING) {
recTask = new RecordingTask2(Main2Activity.this);
recTask.execute();
}
}
});

recordSlideBar.setUpRun(new Runnable() {
@Override
public void run() {
Log.d("MAINACT", "onUp");
recTask.cancel(true);
Log.d("MAINACT", "after cancel\nstatus = "+recTask.getStatus());
while(recTask.getStatus()!= AsyncTask.Status.FINISHED)
{
//waiting for task to end...
}
ShortBuffer shortBuffer = ShortBuffer.allocate(samples.length);
playTask = new PlayingTask(Main2Activity.this,shortBuffer,numOfSamples);

}
});


recordSlideBar
is a custom view I wrote that has an onTouchListener in it while
recordSlideBar.setDownRun(Runnable)
sets a
Runnable
to the
MotionEvent.ACTION_DOWN
and
recordSlideBar.setUpRun(Runnable)
sets a
Runnable
to the
MotionEvent.ACTION_UP
.

Basically, I create the AsyncTask and execute it when
ACTION_DOWN
is called and cancel it when
ACTION_UP
is called...

I'm new to the whole audio capturing techniques so you might see some errors in my code regarding that. Please remember that the issue right now is the AsyncTask ;)

EDIT: Here's a print-screen of the debugger panel after I cancel the task. notice that the AsyncTask status is WAIT, and in the Variables tab,
mCancelled
is
true
and
mStatus
is
Running


Screen-shot: asynctask status=wait,cancelled=true,status=running

Answer

The read function could be blocked in

int numberOfShort = record.read(audioBuffer, 0, audioBuffer.length);

so while(!isCancelled()) may never got called.

you can put Log.d() in the while to check if the code is blocked, like

while(!isCancelled()) { Log.d("RECTASK","start read"); int numberOfShort = record.read(audioBuffer, 0, audioBuffer.length); numSample +=numberOfShort; Log.d("RECTASK","end read"); }