I have a class extending an AsyncTask that sends messages to a WCF web service. Simple messages one by one will work fine, but if I send a message on a new thread that takes 30 seconds to complete, then midway through that I send a quick request it won't execute the AsyncTask until the long one has returned.
I thought the whole idea of AsyncTask was these two messages would run on different threads and therefore wouldn't stack?
Here is my code:
private class RunnableTask extends AsyncTask<RunnableObj, RunnableObj, RunnableObj> {
#Override
protected RunnableObj doInBackground(RunnableObj... params) {
try {
if (params[0].requestBody != (null)) {
params[0].request.body(new JSONObject(params[0].requestBody));
}
params[0].request.asVoid();
return params[0];
}
catch (Throwable e) {
params[0].handler.onFailure(e);
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(RunnableObj runnableObj) {
super.onPostExecute(runnableObj);
runnableObj.handler.onSuccess();
}
}
This is my AsyncTask above.
public void put(final String urlStr, final String requestBody, final HttpResponseHandler httpResponseHandler) {
RunnableObj obj = new RunnableObj();
obj.handler = httpResponseHandler;
obj.request = webb.put(urlStr)
.header(ServiceConstants.SessionTokenHeader, MyApplication.getSessionToken())
.ensureSuccess();
obj.requestBody = requestBody;
new RunnableTask().execute(obj);
}
This is the method I use to call the Async.
As you can see in the method I use to call the service, I initialise a new instance of RunnableTask each time.
How it performs:
The long request will go to the web service and start it's 30 seconds of doing things.
10 seconds later my quick little PUT creates it's object, then the last thing the debugger shows is the break point on the "new RunnableTask().execute(obj);" line and then it just disappears.
20 seconds later the first line of my RunnableTasks doInBackground method will hit and it will perform the PUT.
Please can someone help? Or at least tell me I'm doing something very stupid..
You can execute multiple AsyncTask by using executeOnExecutor
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
new MyAsyncTask().execute();
}
For more check the AsyncTask documentation
According to the AsyncTask Docs:
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
If you truly want parallel execution, you can invoke
executeOnExecutor(java.util.concurrent.Executor, Object[]) with
THREAD_POOL_EXECUTOR.
As you can see, AsyncTasks currently only operate on a single background thread, meaning multiple queued tasks will have to fire one after another. If you would like to execute concurrent tasks, you'll want to follow the instructions above.
Related
My code runs fine, but if I go back to the home screen and return the code stops running on the text view shows nothing. Shouldn't it restart when I return to the page? It throws no errors, but if I eliminate the task and initialize the app it all over again it works but I must stay on that activity because If I go to Home and return it stops.
public class IsstatusActivity extends AppCompatActivity {
JSONParser jsonparser = new JSONParser();
TextView latitude;
TextView longitude;
String lat;
String longit;
JSONObject jobj = null;
private final ReentrantLock lock = new ReentrantLock();
private final Condition tryAgain = lock.newCondition();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.isstatus);
latitude = (TextView) findViewById(R.id.Coordinates);
longitude = (TextView) findViewById(R.id.longitude);
new Retrievedata().execute();
}
class Retrievedata extends AsyncTask<Void, String, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
while (!isCancelled()) {
jobj = jsonparser.makeHttpRequest("http://api.wheretheiss.at/v1/satellites/25544");
String lat = "latitude : " + jobj.getString("latitude");
String longit ="longitude : " + jobj.getString("longitude");
// this will cause onProgressUpdate to be called with lat & long
publishProgress(lat,longit);
// it's okay to sleep within the background thread
Thread.sleep(1500);
}
} catch (InterruptedException e) {
Log.w("RetrieveData", "thread was interrupted", e);
} catch (JSONException e) {
Log.e("RetrieveData", "parse error", e);
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
latitude.setText(values[0]);
longitude.setText(values[1]);
}
}
}
Shouldn't it restart when I return to the page?
Not necessarily. From the AsyncTask documentation:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
So by default the execution of AsyncTasks on versions of Android from Honeycomb onwards is serialised, meaning that only one can execute at any given time.
Looking at Retrievedata, it is set up to loop until it is cancelled (or it is interrupted or encounters a JSON related exception). However, the task isn't ever cancelled in IsstatusActivity (say, in onDestroy()), so barring interruptions or errors it will run forever (or at least until your app is killed).
Part of the reason for this is that AsyncTasks are not tied to the activity lifecycle. That means that they don't get automatically stopped when the activity they were created in finishes - rather, they keep on running until they finish whatever they're tasked with doing. Again, because Retrievedata loops until it's cancelled and this never happens, it will happily block the execution of new instances of Retrievedata created when the IsstatusActivity is subsequently started again (not to mention any other AsyncTask instances you may be using in other activities).
For more background I recommend checking out:
https://stackoverflow.com/a/13147992/2259854
http://blog.danlew.net/2014/06/21/the-hidden-pitfalls-of-asynctask/
You may be able to solve the issue by cancelling the task when you finish the activity, but as other posters have stated it may be better to avoid using AsyncTask at all for this kind of task. My understanding of AsyncTask is that it is best used for short running operations - indeed, this is what the documentation says:
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.
As an aside, there's another issue with the code posted. As Retrievedata is an inner class (as opposed to being static) of IsstatusActivity, each instance of it will keep an implicit reference to the instance of IsstatusActivity it was created in. This means that even after you finish any given instance of IsstatusActivity, it will remain in memory until the Retrievedata instance created within it has finished and can be garbage collected. As per the above, this will never happen because the task runs forever.
This is called activity/context leaking. Every time IsstatusActivity is started and finished, the instance will hang around consuming memory until the app is killed. Activities are heavy objects so this is definitely something to be avoided.
Hope that helps!
In my very first android project, I do some data manipulation, so I use multi-threading approach.
In MainActivity, I created multiple Runnable object and use ExecutorService to run all the threads. As my understanding, all threads are put in message queue and executed in turn. And the because the main thread is already in the queue, it will be executed before starting other threads. Is there any way that I can make the main thread wait for other threads to finish and then continue?
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//call MyFunction here
}
private List<Pair[]> myFunction(int dataInput) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(12);
MyTask MyTask = new MyTask();
for (int i = 0; i < gallerySize; ++i) {
final int index = i;
Runnable runnable = MyTask.runLongOperationWithThread(new MyTask.DataCallback(){
#Override
public void onSuccess(double[] scores) {
// get data back to main thread
}
#Override
public void onError(Exception ex) {
//TODO: log this error out to file
}
});
executorService.execute(runnable);
}
// try to get back all data from multi threading and do some operations
return returnList;
}
Do Looper and Handler help in this case?
And please correct me if I have any misunderstanding in android concept and threading.
Thanks.
In Android, stopping main thread is discouraged. The system will tell the user that the app is not responding. However, you can "notify" the main thread that the background thread has finished its work. Once the main thread knows this, it will do something. It is common in Android, it is what AsyncTask for.
However, AsyncTask is used for a simple one thread. In your case, one of the solution is to combine ExecutorService and AsyncTask. In doInBackground method of AsyncTask instance you make, use ExecutorService like usual, and wait it to finish by either shutdown(); awaitTermination() or invokeAll(). Read this question/answer for more information about how to wait ExecutorService to finish.
private class WrappingTask extends AsyncTask<Void, Void, Exception> {
protected Exception doInBackground(Void... args) {
ExecutorService taskExecutor = Executors.newFixedThreadPool(12);
for (. . .) {
taskExecutor.execute(new MyTask(. . .));
}
taskExecutor.shutdown();
try {
taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
. . .
return e;
}
return null;
}
protected void onPostExecute(Exception error) {
// Notify the user that the task has finished or do anything else
// and handle error
}
}
In case of long running task
AsyncTask is a handy class to make threading and communicating (to main thread) easier. The problem for long running task is that the user can leave the Activity (and then come again), or there is an incoming call, etc. If you don't handle this Activity lifecycle with care, it is so "dangerous", AsyncTask does not handle this.
Long running task should be run in a Service. Note that Service is also run in the main thread, so the approach would be the same, unless you use IntentService. In case of IntentService, just execute all of the threads (formerly in doInBackground) in the onHandleIntent method and wait it there, this method is called on a worker thread.
Communicating Service with Activity and maintaining consistency of Activity's state through its lifecycle is a long story. You better read the documentation in "a full concentration" with a cup of coffee :D. This might helps:
Managing the Activity Lifecycle
Best Practices for Background Jobs
I have created an AsyncTsak class in my project which downloads some information from web server. I am sure that it works well because when it is called by onCreate() , I can see the result . But unfortunately when I call it again via a button it doesn't work.
I am not sure but i think i have read somewhere about this problem . It said , we are permitted to use AsyncTask class only once.
AsyncTask class
class DownloadAdvertismentLevel2 extends AsyncTask<String,String,String>{
String [] ImageInformation=null;
protected void onPreExecute(){
// do nothing !
}
protected String doInBackground(String...Urls){
String Data="";
HttpURLConnection urlConnection = null;
try{
URL myUrl=new URL("http://10.0.2.2:80/Urgence/ads.aspx?Action=DownloadIds&TB="+TopBoundry+"&LB="+LowBoundry);
urlConnection = (HttpURLConnection)myUrl.openConnection();
BufferedReader in = new BufferedReader (new InputStreamReader(urlConnection.getInputStream()));
String temp="";
// Data is used to store Server's Response
while((temp=in.readLine())!=null)
{
Data=Data+temp;
}
}
catch(Exception ex){
Log.d("Er>doInBackground", ex.toString());
urlConnection.disconnect();
}
finally{
urlConnection.disconnect();
}
return Data;// it sends Result to onPostExcute
}
protected void onPostExecute(String Data){
createPhotoAlbum();
pb.closedProg();
}
}
onCreate
Here I don't have any problem . It works fine
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ads);
new DownloadAdvertismentLevel2().execute();
}
Via Button
ButtonSeeMore.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
Counting();
}});
Counting
public void Counting(){
if(TotalRows-6>0){
TopBoundry=TotalRows;
LowBoundry=TotalRows-6;
TotalRows=LowBoundry;
}
new DownloadAdvertismentLevel2().execute();
}
Please consider that I need to use this class till it shows the information. What would you suggest ?
To expand on what JVN said about AsyncTask
Each instance of Async task can only be executed once.
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
http://developer.android.com/reference/android/os/AsyncTask.html (Under 'Threading Rules')
This doesn't stop you making a new instance -
public void Counting(){
if(TotalRows-6>0){
TopBoundry=TotalRows;
LowBoundry=TotalRows-6;
TotalRows=LowBoundry;
}
new DownloadAdvertismentLevel2().execute();
new DownloadAdvertismentLevel2().execute();
}
^ The code above will run your task twice.
Your code looks fine.
I would guess that the problem is (in order of likelihood)
1) On click isn't working
2) Post Execute isn't working as expected
3) The server response isn't being read correctly
4) Your Server isn't handling the request properly
But this would be obvious if you run your debugger or add some extra log outputs
1) I think you might be able to use the Async task only once in the class. But definitely it can be called multiple times.
2) please check if your button onclicklistener() function is really getting called on button click. try some logs in that.
because the code seems to be fine.
To allow multiple asycnh tasks run at the same time you need to use the 'executeOnExceuter mechanism:
See this note from the Android doucmentation:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
An example invocation would look like (this particular example is from a video manipulation app - the 'distributionTask' is an instance of a class that extends AsynchTask):
//Now execute synch task - to allow multiple AsynchTasks execute in parallel the
//'executeOnExecutor' call is required. It needs to be used with caution to avoid the usual synchronization issues and also
//to avoid too many threads being created
distributionTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Environment.getExternalStorageDirectory() + "/videoChunk_"+i+".mp4");
I am devloping an android application with multiple tabs. My tabs have the following.
1st tab - It shows archives for a magazine in a grid view. The intention is to when I tap a grid view Item, I would need to download a large file from a web site. For this I have created a service that has an ASYNC task that would download the file. I have shown the code.
public class DownloadArchivesService extends BaseService {
private static final String TAG = "DownloadArchivesServices";
private static final String STOP = "stop_modificator";
public static final String ALREADY_RUNNING= "DownloadArchivesService_Already_Running";
public static final String ARCHIVES_PERCENTAGE_PROGRESS ="Archives_Percentage_Progress";
public static final String UPDATE_ARCHIVES_PROGRESS = "Update_Archives_Progress";
#Override
public void onCreate() {
getPreferences().edit().putBoolean(ALREADY_RUNNING, true).commit();
application = (JfwApplication)this.getApplicationContext();
context = this;
session = application.getSession();
calendar = Calendar.getInstance();
dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
magazineManager = new MagazineManager(this);
//magazine = new Magazine(fileName, fileName, "", "", this);
new AsyncTask<String, Integer, String>() {
NumberFormat formater = NumberFormat.getPercentInstance(Locale.getDefault());
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
// I do my download here }
#Override
protected void onProgressUpdate(Integer... p) {
if (isCancelled()) {
return;
}
//I udpate my progress bar here }
#Override
protected void onPostExecute(String result) {
Intent intentInvalidate = new Intent(ArchivesActivity.GRID_INVALIDATE);
sendBroadcast(intentInvalidate);
super.onPostExecute(result);
}
}.execute();
super.onCreate();
}
When I navigate to my second tab, I would need to download a few photos from the website and display it on the screen. I have written a service similar to the above - DownloadImagesService.
But the problem is when the above mentioned DownloadArchivesService is running,(i.e., when I had
tapped a grid view item to invoke the DownloadArchivesService) and navigate to the second tab to download the photos,The DownloadImageService(The service to download photos) is not invoked and it waits till the DownloadArchivesService is completed. This would make the user to wait for sometime before the first service completes. I understand that the services would run in main thread and one service would need to wait for an already executing service to execute.
Is there a way around to run different services(or background tasks) concurrently without waiting for a previously excecuting services.
I did go through various documents on the Services, Intent services and threads. But I could not undertand this particular thing on how andorid handles concurrent services.
I would greatly appreciate any help on this. Please let me know If My explanation is not clear.
Thanks.
From Android 4.0+ AsyncTask are given a single thread of execution. So 2nd AsyncTask will run after the completion of first.
See Multiple Async Task not running in android 4.1 for how to handle them.
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
http://developer.android.com/reference/android/os/AsyncTask.html
I would suggest you to use executor for this purpose
http://developer.android.com/reference/java/util/concurrent/Executor.html
I have AsyncTask that processes some background HTTP stuff. AsyncTask runs on schedule (Alarms/service) and sometime user executes it manually.
I process records from SQLite and I noticed double-posts on server which tells me that sometime scheduled task runs and at the same time user runs it manually causing same record to be read and processed from DB twice. I remove records after they processed but still get this.
How should I handle it ? Maybe organize some kind of queing?
You can execute your AsyncTask's on an Executor using executeOnExecutor()
To make sure that the threads are running in a serial fashion please use: SERIAL_EXECUTOR.
Misc: How to use an Executor
If several activities are accessing your DB why don't create a sort of gateway database helper and use the synchronized block to ensure only one thread has access to it at an instant
Or, you can try this to see if the Task is currently running or not:
if (katitsAsyncTask.getStatus().equals(AsyncTask.Status.FINISHED))
katitsAsyncTask.execute();
else
// wait until it's done.
Initialize the AsyncTask to null. Only create a new one if it is null. In onPostExecute, set it to null again, at the end. Do the same in onCancelled, in case the user cancels this. Here's some untested code to illustrate the basic idea.
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class FooActivity extends Activity {
private class MyAsyncTask extends AsyncTask<Foo, Foo, Foo> {
#Override
protected void onPostExecute(Foo foo) {
// do stuff
mMyAsyncTask = null;
}
#Override
protected void onCancelled() {
// TODO Auto-generated method stub
mMyAsyncTask = null;
}
#Override
protected Foo doInBackground(Foo... params) {
try {
// dangerous stuff
} catch (Exception e) {
// handle. Now we know we'll hit onPostExecute()
}
return null;
}
}
private MyAsyncTask mMyAsyncTask = null;
#Override
public void onCreate(Bundle bundle) {
Button button = (Button) findViewById(R.id.b2);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mMyAsyncTask == null) {
mMyAsyncTask = new MyAsyncTask();
mMyAsyncTask.execute(null);
}
}
});
}
}
I know this was a while ago now, and you have solved your problem. but I just had a similar problem. Reno's suggestion put me on the right track, but for those who have been finding it difficult to fill in the gaps. Here is how I overcame a similar issue to that of katit's.
I wanted a particular AsyncTask to only run if it was not currently running. And as a forward from Reno's suggestion, the AsyncTask interface has been created to handle all the nitty gritty processes in properly dealing with threads for Android. Which means, the Executor is built in. As this blog suggests:
"When execute(Object.. params) is invoked on an AsyncTask the task is executed in a background thread. Depending on the platform AsyncTasks may be executed serially (pre 1.6 and potentially again in 4+), or concurrently (1.6-3.2).
To be sure of running serially or concurrently as you require, from API Level 11 onwards you can use the executeOnExecutor(Executor executor, Object.. params) method instead, and supply an executor. The platform provides two executors for convenience, accessable as AsyncTask.SERIAL_EXECUTOR and AsyncTask.THREAD_POOL_EXECUTOR respectively. "
So with this in mind, you can do thread blocking via the AsyncTask interface, it also implies you can simply use the AsyncTasks.getStatus() to handle thread blocking, as DeeV suggests on this post.
In my code, I managed this by:
Creating a global variable defined as:
private static AsyncTask<String, Integer, String> mTask = null;
And in onCreate, initialising it as an instance of my AsyncTask called CalculateSpecAndDraw:
mTask = new CalculateAndDrawSpec();
Now when ever I wish to call this AsyncTask I surround the execute with the following:
if(mTask.getStatus() == AsyncTask.Status.FINISHED){
// My AsyncTask is done and onPostExecute was called
mTask = new CalculateAndDrawSpec().execute(Integer.toString(progress));
}else if(mTask.getStatus() == AsyncTask.Status.PENDING){
mTask.execute(Integer.toString(progress));
}else{
Toast.makeText(PlaySpecActivity.this, "Please Wait..", 1000).show();
}
This spawns a new thread if it is finished, or if the thread state is PENDING, the thread is defined but has not been started we start it. But otherwise if the thread is running we don't re-run it, we simply inform the user that it is not finished, or perform what ever action we wish. Then if you wanted to schedule the next event rather than just block it from re running, take a look at this documentation on using executors.
How about just wrapping your check-what-to-send and send-it logic in a synchronized method? This approach seems to work for us.
try having some instance boolean value that gets set to "true" on the asynctask's preexecute then "false" on postexecute. Then maybe in doinbackground check if that boolean is true. if so, then call cancel on that particular duplicate task.
You could keep the state of the task in shared preferences. Check the value (Boolean perhaps) before starting the task. Set the state to finished(true?) in onPostExecute and false in onPreExecute or in the constructor