AsyncTask.get() no progress bar - android

My app sends data to the server. It generally works fine until the user is in a bad signal area. If the user is in a good signal area the the following code works fine and the data is sent.
String[] params = new String[]{compID, tagId, tagClientId, carerID,
formattedTagScanTime, formattedNowTime, statusForWbService, getDeviceName(), tagLatitude, tagLongitude};
AsyncPostData apd = new AsyncPostData();
apd.execute(params);
.
private class AsyncPostData extends AsyncTask<String, Void, String> {
ProgressDialog progressDialog;
String dateTimeScanned;
#Override
protected void onPreExecute()
{
// progressDialog= ProgressDialog.show(NfcscannerActivity.this,
// "Connecting to Server"," Posting data...", true);
int buildVersionSdk = Build.VERSION.SDK_INT;
int buildVersionCodes = Build.VERSION_CODES.GINGERBREAD;
Log.e(TAG, "buildVersionSdk = " + buildVersionSdk
+ "buildVersionCodes = " + buildVersionCodes);
int themeVersion;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {
themeVersion = 2;
}else{
themeVersion = 1;
}
progressDialog = new ProgressDialog(NfcscannerActivity.this, themeVersion);
progressDialog.setTitle("Connecting to Server");
progressDialog.setMessage(" Sending data to server...");
progressDialog.setIndeterminate(true);
try{
progressDialog.show();
}catch(Exception e){
//ignore
}
};
#Override
protected String doInBackground(String... params) {
Log.e(TAG, "carerid in doinbackground = " + params[3] + " dateTimeScanned in AsyncPost for the duplecate TX = " + params[4]);
dateTimeScanned = params[4];
return nfcscannerapplication.loginWebservice.postData(params[0], params[1], params[2], params[3], params[4],
params[5], params[6], params[7] + getVersionName(), params[8], params[9]);
}
#Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
try{
progressDialog.dismiss();
}catch(Exception e){
//ignore
}
if( result != null && result.trim().equalsIgnoreCase("OK") ){
Log.e(TAG, "about to update DB with servertime");
DateTime sentToServerAt = new DateTime();
nfcscannerapplication.loginValidate.updateTransactionWithServerTime(sentToServerAt,null);
nfcscannerapplication.loginValidate.insertIntoDuplicateTransactions(dateTimeScanned);
tagId = null;
tagType = null;
tagClientId = null;
//called to refresh the unsent transactions textview
onResume();
}else if(result != null && result.trim().equalsIgnoreCase("Error: TX duplicated")){
Log.e(TAG, "response from server is Duplicate Transaction ");
//NB. the following time may not correspond exactly with the time on the server
//because this TX has already been processed but the 'OK' never reached the phone,
//so we are just going to update the phone's DB with the DupTX time so the phone doesn't keep
//sending it.
DateTime sentToServerTimeWhenDupTX = new DateTime();
nfcscannerapplication.loginValidate.updateTransactionWithServerTime(sentToServerTimeWhenDupTX,null);
tagId = null;
tagType = null;
tagClientId = null;
}else{
Toast.makeText(NfcscannerActivity.this,
"No phone signal or server problem",
Toast.LENGTH_LONG).show();
}
}
}//end of AsyncPostData
.
The app in bad signal areas tends to show the progress bar for a few minutes before showing a black screen for a while rendering the app unusable.
I thought a way around this would be to do the following.
String[] params = new String[]{compID, tagId, tagClientId, carerID,
formattedTagScanTime, formattedNowTime, statusForWbService, getDeviceName(), tagLatitude, tagLongitude};
AsyncPostData apd = new AsyncPostData();
try {
apd.execute(params).get(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
This will cause the AsyncTask to cancel after 10 seconds, but as it is executing there is a black screen until the data is sent followed by the progressbar for a few millisecs.
Is there a way to show the progressbar whilst executing an AsyncTask.get()?
thanks in advance. matt.
Also are there any ideas why the black screen comes when the user is in bad signal area and therefor no response from the server. This senario seems to cause the app alot of problems where it's behavior is unusual afterwards like sending extra transactions at a later date.
[edit1]
public class SignalService extends Service{
NfcScannerApplication nfcScannerApplication;
TelephonyManager SignalManager;
PhoneStateListener signalListener;
private static final int LISTEN_NONE = 0;
private static final String TAG = SignalService.class.getSimpleName();
#Override
public void onCreate() {
super.onCreate();
// TODO Auto-generated method stub
Log.e(TAG, "SignalService created");
nfcScannerApplication = (NfcScannerApplication) getApplication();
signalListener = new PhoneStateListener() {
public void onSignalStrengthChanged(int asu) {
//Log.e("onSignalStrengthChanged: " , "Signal strength = "+ asu);
nfcScannerApplication.setSignalStrength(asu);
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
// TODO Auto-generated method stub
Log.e(TAG, "SignalService destroyed");
SignalManager.listen(signalListener, LISTEN_NONE);
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// TODO Auto-generated method stub
Log.e(TAG, "SignalService in onStart");
SignalManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
SignalManager.listen(signalListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTH);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}

You do not need a timer at all to do what you're attempting (for some reason I thought you were trying to loop the AsyncTask based on your comments above which resulted in mine.). If I understand correctly you're issue is with the loss of service. You have an AsyncTask that you start which may or may not finish depending on certain conditions. Your approach was to use get and cancle the task after a fixed time in the event that it did not finish executing before then - the assumption being if the task didn't finish within the 10 second cut off, service was lost.
A better way to approach this problem is to use a boolean flag that indcates whether network connectivity is available and then stop the task from executing if service is lost. Here is an example I took from this post (I apologize for the formatting I'm on a crappy computer with - of all things - IE8 - so I can't see what the code looks like).
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
private final ProgressDialog progressDialog;
public MyTask(Context ctx) {
progressDialog = gimmeOne(ctx);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
// actually could set running = false; right here, but I'll
// stick to contract.
cancel(true);
}
});
}
#Override
protected void onPreExecute() {
progressDialog.show();
}
#Override
protected void onCancelled() {
running = false;
}
#Override
protected Void doInBackground(Void... params) {
while (running) {
// does the hard work
}
return null;
}
// ...
}
This example uses a progress dialog that allows the user to cancle the task by pressing a button. You're not going to do that but rather you're going to check for network connectivty and set the running boolean based on whether your task is connected to the internet. If connection is lost - running will bet set to false which will trip the while loop and stop the task.
As for the work after the task complete. You should NEVER use get. Either (1) put everything that needs to be done after the doInBackgroundCompletes in onPostExecute (assuming its not too much) or (2) if you need to get the data back to the starting activity use an interface. You can add an interface by either adding as an argument to your tasks constructor or using a seperate method that sets the interface up. For example
public void setInterface(OnTaskComplete listener){
this.listener = listener;
}
Where OnTaskComplete listener is declared as an instance variable in your AsyncTask. Note the approach I am describing requires using a seperate AsyncTask class. Your's is private right now which means you need to change your project a little.
UPDATE
To check connectivity I would use something like this.
public boolean isNetworkOnline() {
boolean status=false;
try{
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getNetworkInfo(0);
if (netInfo != null && netInfo.getState()==NetworkInfo.State.CONNECTED) {
status= true;
}else {
netInfo = cm.getNetworkInfo(1);
if(netInfo!=null && netInfo.getState()==NetworkInfo.State.CONNECTED)
status= true;
}
}catch(Exception e){
e.printStackTrace();
return false;
}
return status;
}
You can check to see if there is an actual network connection over which your app can connect to ther server. This method doesn't have to be public and can be part of you're AsyncTask class. Personally, I use something similar to this in a network manager class that I use to check various network statistics (one of which is can I connect to the internet).
You would check connectivity before you started executing the loop in your doInBackground method and then you could periodicly update throughout the course of that method. If netowkr is available the task will continue. If not it will stop.
Calling the AsyncTask built in cancle method is not sufficient becuase it only prevent onPostExecute from running. It does not actually stop the code from execting.

Related

How to set a fixed time if AsyncTask is not finished

I have an asynctask and I'm looking to do a wait if the async is taking too long, I'm downloading some data of the database , but I don't want to have the user looping around the progressdialog, I want to set a fixed time, lets say I'm downloading a file but is taking forever, so I tell the user, "hey, check your internet connection and try again" I'm looking to do this with a timer, but I'm kinda stuck, this is where I do my asynctask
private class DownloadFilesTask extends AsyncTask<Void, Void, Void> {
String s;
public DownloadFilesTask(String s){
this.s = s;
}
#Override
protected Void doInBackground(Void... voids) {
DownloadMethod(s);
return null;
}
}
so let's say I want to execute that downloadmethod for a fixed time, 10 or 20 seconds, if the file is not downloaded at that time I return a message to the user saying that he needs to check for hes internet.
You can use handler to run after a definite amount time and maintain a boolean flag which you can update in postExecute function of async task.
In your activity/fragment class:
new Handler().postDelayed(new Runnable(){
public void run(){
//Check whether the flag has been updated or not
},1000)
You can use BroadcastReceiver to listen your internet connection. Here is an example:
public class NetworkControl extends BroadcastReceiver {
static boolean isConnected = false;
#Override
public void onReceive(final Context context, final Intent intent) {
isNetworkAvailable(context);
}
private boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
if(!isConnected){
isConnected = true;
Toast.makeText(context, "You're online!!", Toast.LENGTH_LONG).show();
}
return true;
}
}
}
}
isConnected = false;
Toast.makeText(context, "Connection interrupted.", Toast.LENGTH_LONG).show();
return false;
}
}
Also you need some permissions in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
Then start the service in your activity.
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
receiver = new NetworkControl();
registerReceiver(receiver, filter);
If you are using HttpURLConnection to download the file then you can do something like this:
private class DownloadFilesTask extends AsyncTask<String, Integer, Integer> {
#Override
protected Integer doInBackground(String... ulr) {
URL url = null;
try {
url = new URL(ulr[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setConnectTimeout(2000); //Timeout
//...Other codes for downloading
return 101;
} catch (java.net.SocketTimeoutException e) {
return 102;
} catch (MalformedURLException e) {
return 103;
} catch (IOException e) {
return 104;
}
}
#Override
protected void onPostExecute(Integer result) {
if(result == 102) {
Toast.makeText(getApplicationContext(), "Connection Timeout.", Toast.LENGTH_LONG).show();
}
}
}
First thing I want to say that is, while running downloading task it's not a good practice, to message a user that check your internet connection or
no internet connection. Because in this condition, if user do switch off then on network connection then your downloading task restarts again and takes whole time again. So, avoid this types of messages.
Now about solution, after execute background task you can check your task is running or completed. If it takes too much time
then show a message. For example,
YourBackgroundTask task = new YourBackgroundTask();
task.execute();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (task.getStatus == AsyncTask.Status.RUNNING){
Toast.makeText(this, "Please wait...", Toast.LENGTH_SHORT).show();
}
}
},20000); // time in milisecond
And if you want to repeat this, you can easily re-run handler.
Here is a basic idea you can try if it works
private class DownloadFilesTask extends AsyncTask<Void, Integer, Void> implements TimerTask{
String s;
Timer timer;
Object objectResult;
public DownloadFilesTask(String s){
this.s = s;
timer = new Timer();
}
#Override
protected Void doInBackground(Void... voids) {
objectResult = DownloadMethod();
return null;
}
private Object DownloadMethod() {
//here implement the download logic and return the object
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
// your code to update progress
}
public void checkProgress(){
timer.schedule(this,2000);
}
#Override
public void run() {
if (objectResult!=null){
//download completed
}else{
//show dialog here and schedule a task again
timer.schedule(this,2000);
}
}
}

Static method call from AsyncTask is async?

I'm trying to use a AsyncTask to check if internet connection is available and then doing some stuff.
My problem is while the comprobation is being executed I canĀ“t interacting with the UI. The "progressbar" is on front and if I try to click on a button the UI doesn't respond
This is the call to AsyncTask
#Override
public void onStart(){
super.onStart();
AsyncHttpHandler check = new AsyncHttpHandler(**paramaters**);
check.execute("checkshared");
}
This is the code of AsyncTask
public class AsyncHttpHandler extends AsyncTask<String, Integer, String>{
public AsyncHttpHandler(Context c, Intent i, Bundle _data, String _language){
ctx = c;
intent = i;
data = _data;
language = _language;
startClock();
}
public void startClock(){
mDialog = new ProgressDialog(ctx,R.style.MyTheme);
mDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small);
mDialog.setCancelable(false);
mDialog.show();
}
#SuppressWarnings("unchecked")
#Override
protected String doInBackground(String... peticion) {
String response = "null";
//call to check if internet connection is available
if(Utils.isInternetConn(ctx)){
try {
// do stuff
response = "...";
}
else response = "offline";
return response;
}
}
protected void onPostExecute(String response) {
if(mDialog != null) mDialog.dismiss();
if(!response.equals("offline")){
// do stuff
}
else Toast.makeText(ctx, ctx.getResources().getString(ctx.getResources().getIdentifier(language+"_toast_nointernet", "string", ctx.getPackageName())), Toast.LENGTH_LONG).show();
}
}
And this is the method to check if the internet connection is available:
public static Boolean isInternetConn(Context ctx){
ConnectivityManager connec = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifi = connec.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
NetworkInfo mobile = connec.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean conectado = false;
if((wifi != null && wifi.isConnectedOrConnecting()) || (mobile != null && mobile.isConnectedOrConnecting())){
try {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
URL myurl = new URL("http://www.google.com");
URLConnection connection;
connection = myurl.openConnection();
connection.setConnectTimeout(2000);
connection.setReadTimeout(2000);
HttpURLConnection httpConnection = (HttpURLConnection) connection;
int responseCode = -1;
responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
conectado = true;
httpConnection.disconnect();
}
else {
httpConnection.disconnect();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return conectado;
}
What am I doing wrong?
The problem is not in your AsyncTask calling static method. That will also execute async. Problem is that you are showing progress dialog until your task finishes. And, of course, while your progress dialog is visible you will not be able to interact with the UI. If you want to interact with the UI, show your progress in another way, not with dialog.
You display a progressDialog on top of the screen. The progress dialog takes the UI interaction, so the buttons underneath don't respond. If you use a progress bar in your UI instead of a progress dialog, your buttons will work.
If you want to show the progress of your task to the user, use the method onProgressUpdate, as it has been implemented to run on the UI thread.
Now, each time you want to show the progress from doInBackground, call it by passing an argument. To be clear, AsyncTask (String, Integer, String) uses argument types:
First is the type of argument you pass when you call execute
Second is the type of the argument you pass to onProgressUpdate
Third is the type of argument you pass to onPostExecute
I'm not sure if I understood what is your problem, but it seems to me, that you cannot interact with UI because the ProgressDialog is not cancelable, and it's in front of everything else.
You will only have feedback when dialog is dismissed.

Running AsyncTask with button performClick

I have a fragment that contains a Button btn_connect that when it is pressed a WiFi Direct connection is established between 2 devices. This fragment implements ConnectionInfoListener. So it has onConnectionInfoAvailable function where I want to execute an AsyncTask class. The problem that I have is that in one Activity, I am doing:
fragment.mContentView.findViewById(R.id.btn_connect).performClick();
And the button is being clicked and the connection is established so the code goes into the onConnectionInfoAvailable function but the AsyncTask is not being executed.
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
//..code..
Log.d("Test 1", "Test 1");
new MasterServerTask().execute();
}
public class MasterServerTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
//**************
Log.d("IM INSIDE ASYNCTASK CLASS", "SOCKET");
try {
serverSocket = new ServerSocket(8090);
} catch (IOException e) {
e.printStackTrace();
}
while (true) {//wait for clients
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("ACCEPTED A SLAVE DEVICE "+num_clients, "ACCEPTED A SLAVE DEVICE "+num_clients);
num_clients++;
OutputStream os=null;
try {
os = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
proxy.addSlaveOutputStream(os);
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {//Phone that connects first is NOT the group owner
// port = Integer.parseInt(editTextPort.getText().toString());
Log.d("IM IN THE OTHER FRAGMENT", "Connect");
WifiP2pConfig config = new WifiP2pConfig();
config.groupOwnerIntent = 0;
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel",
"Connecting to :" + device.deviceAddress, true, true
);
((DeviceActionListener) getActivity()).connect(config);
}
});
Is there an easy workaround solution for this?
Check how/where you are calling WifiP2pManager.initialize() to create the WifiP2pManager.Channel object. The Looper you provide it is the one which will receive all callbacks for your instance of WifiP2pManager.ConnectionInfoListener. If you are giving it a background thread then the AsyncTask will not execute - it must be started from the main (UI) thread.
The comments on the question were really helpful. The reason why the AsyncTask was not getting executed is because it was called from another task that is currently being executed. So in order for it to work, I replaced the AsyncTask with Thread classes. All the code in the doInBackground() was placed inside the thread's run() function. Now the performClick() executes a Thread, not an AsyncTask and it worked.

Android - Call an Asynctask from an Asynctask with infinity loop

I have 2 Asynctask, 1 for get data (location) from server then set a marker on map with this location and another call 1st Asyntask in a loop for updating location.
Here my code:
public class AsynComp extends AsyncTask<Void, Void, Void> {
ProgressDialog taxiDialog;
#Override
protected Void doInBackground(Void... params) {
jsonComp = new JSONComp(find_url);
find_status = jsonComp.getJsonStatus(txt_search);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (find_status.equals("2013")) {
Toast.makeText(getBaseContext(), "no result",
Toast.LENGTH_SHORT).show();
} else if (find_status.equals("2012")) {
for (Marker marker:markers){
if(marker.getTitle().equals(compFollow)){
marker.remove();
}
}
for (int i=0; i<number;i++){
comp = new Comp(jsonComp.getJsondata(i));
SetMarkerComp(comp);
try {
Thread.sleep(1400);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public class AsynFollow extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
if (!taxiFollow.equals("")) {
number = 1;
txt_search = compFollow;
find_url = "http://192.111.125.80:8001/Default.aspx?username="
+ Id + "&password=" + Pass + "&sohieuxe="+txt_search;
while (!stop){
new AsynComp().execute();
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
taxiFollow = "";
}
return null;
}
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (!compFollow.equals("")) {
Toast.makeText(getBaseContext(), "Follow "+compFollow, Toast.LENGTH_SHORT).show();
} else {
iv_theodoi.setVisibility(View.VISIBLE);
iv_theodoif.setVisibility(View.GONE);
Toast.makeText(getBaseContext(), "Plz choose a marker", Toast.LENGTH_SHORT).show();
}
}
}
And i have 2 buuton, 1 to call AsynFollow.execute(), another to stop it.
This code can run but app will force close after awhile.
Any solution? thanks.
P/s: i'm a newbie in android.
You shoulnd you asyncTask for this. For repetitive action, like changing status in some interval, use Timer class. In this way you can implement repetitive action which can be repeated in intervals.
In this way you can stop this time by on click listener. You can run two times and specify it's realtions using other variables.
If you're newbe, you should read about multitasking in Android: Timer, AsyncTask, Handler.
In my opinion this docs will tell you much more than thousands of comments in stackoverflow.

Ideal way to cancel an executing AsyncTask

I am running remote audio-file-fetching and audio file playback operations in a background thread using AsyncTask. A Cancellable progress bar is shown for the time the fetch operation runs.
I want to cancel/abort the AsyncTask run when the user cancels (decides against) the operation. What is the ideal way to handle such a case?
Just discovered that AlertDialogs's boolean cancel(...); I've been using everywhere actually does nothing. Great.
So...
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
private final ProgressDialog progressDialog;
public MyTask(Context ctx) {
progressDialog = gimmeOne(ctx);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
// actually could set running = false; right here, but I'll
// stick to contract.
cancel(true);
}
});
}
#Override
protected void onPreExecute() {
progressDialog.show();
}
#Override
protected void onCancelled() {
running = false;
}
#Override
protected Void doInBackground(Void... params) {
while (running) {
// does the hard work
}
return null;
}
// ...
}
If you're doing computations:
You have to check isCancelled() periodically.
If you're doing a HTTP request:
Save the instance of your HttpGet or HttpPost somewhere (eg. a public field).
After calling cancel, call request.abort(). This will cause IOException be thrown inside your doInBackground.
In my case, I had a connector class which I used in various AsyncTasks. To keep it simple, I added a new abortAllRequests method to that class and called this method directly after calling cancel.
The thing is that AsyncTask.cancel() call only calls the onCancel function in your task. This is where you want to handle the cancel request.
Here is a small task I use to trigger an update method
private class UpdateTask extends AsyncTask<Void, Void, Void> {
private boolean running = true;
#Override
protected void onCancelled() {
running = false;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
onUpdate();
}
#Override
protected Void doInBackground(Void... params) {
while(running) {
publishProgress();
}
return null;
}
}
Simple: don't use an AsyncTask. AsyncTask is designed for short operations that end quickly (tens of seconds) and therefore do not need to be canceled. "Audio file playback" does not qualify. You don't even need a background thread for ordinary audio file playback.
The only way to do it is by checking the value of the isCancelled() method and stopping playback when it returns true.
This is how I write my AsyncTask
the key point is add Thread.sleep(1);
#Override protected Integer doInBackground(String... params) {
Log.d(TAG, PRE + "url:" + params[0]);
Log.d(TAG, PRE + "file name:" + params[1]);
downloadPath = params[1];
int returnCode = SUCCESS;
FileOutputStream fos = null;
try {
URL url = new URL(params[0]);
File file = new File(params[1]);
fos = new FileOutputStream(file);
long startTime = System.currentTimeMillis();
URLConnection ucon = url.openConnection();
InputStream is = ucon.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
byte[] data = new byte[10240];
int nFinishSize = 0;
while( bis.read(data, 0, 10240) != -1){
fos.write(data, 0, 10240);
nFinishSize += 10240;
**Thread.sleep( 1 ); // this make cancel method work**
this.publishProgress(nFinishSize);
}
data = null;
Log.d(TAG, "download ready in"
+ ((System.currentTimeMillis() - startTime) / 1000)
+ " sec");
} catch (IOException e) {
Log.d(TAG, PRE + "Error: " + e);
returnCode = FAIL;
} catch (Exception e){
e.printStackTrace();
} finally{
try {
if(fos != null)
fos.close();
} catch (IOException e) {
Log.d(TAG, PRE + "Error: " + e);
e.printStackTrace();
}
}
return returnCode;
}
Our global AsyncTask class variable
LongOperation LongOperationOdeme = new LongOperation();
And KEYCODE_BACK action which interrupt AsyncTask
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
LongOperationOdeme.cancel(true);
}
return super.onKeyDown(keyCode, event);
}
It works for me.
I don't like to force interrupt my async tasks with cancel(true) unnecessarily because they may have resources to be freed, such as closing sockets or file streams, writing data to the local database etc. On the other hand, I have faced situations in which the async task refuses to finish itself part of the time, for example sometimes when the main activity is being closed and I request the async task to finish from inside the activity's onPause() method. So it's not a matter of simply calling running = false. I have to go for a mixed solution: both call running = false, then giving the async task a few milliseconds to finish, and then call either cancel(false) or cancel(true).
if (backgroundTask != null) {
backgroundTask.requestTermination();
try {
Thread.sleep((int)(0.5 * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (backgroundTask.getStatus() != AsyncTask.Status.FINISHED) {
backgroundTask.cancel(false);
}
backgroundTask = null;
}
As a side result, after doInBackground() finishes, sometimes the onCancelled() method is called, and sometimes onPostExecute(). But at least the async task termination is guaranteed.
With reference to Yanchenko's answer on 29 April '10:
Using a 'while(running)' approach is neat when your code under 'doInBackground' has to be executed multiple times during every execution of the AsyncTask. If your code under 'doInBackground' has to be executed only once per execution of the AsyncTask, wrapping all your code under 'doInBackground' in a 'while(running)' loop will not stop the background code (background thread) from running when the AsyncTask itself is cancelled, because the 'while(running)' condition will only be evaluated once all the code inside the while loop has been executed at least once. You should thus either
(a.) break up your code under 'doInBackground' into multiple 'while(running)' blocks or
(b.) perform numerous 'isCancelled' checks throughout your 'doInBackground' code, as explained under "Cancelling a task" at https://developer.android.com/reference/android/os/AsyncTask.html.
For option (a.) one can thus modify Yanchenko's answer as follows:
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
//...
#Override
protected void onCancelled() {
running = false;
}
#Override
protected Void doInBackground(Void... params) {
// does the hard work
while (running) {
// part 1 of the hard work
}
while (running) {
// part 2 of the hard work
}
// ...
while (running) {
// part x of the hard work
}
return null;
}
// ...
For option (b.) your code in 'doInBackground' will look something like this:
public class MyTask extends AsyncTask<Void, Void, Void> {
//...
#Override
protected Void doInBackground(Void... params) {
// part 1 of the hard work
// ...
if (isCancelled()) {return null;}
// part 2 of the hard work
// ...
if (isCancelled()) {return null;}
// ...
// part x of the hard work
// ...
if (isCancelled()) {return null;}
}
// ...

Categories

Resources