AsyncTask Android in For Structure - android

I want to implement something like the following on Android:
for(int i=0; i<num; i++){
//a,b.. are changing
aTask = new myAsyncTask(a,b..);
aTask.execute();
//Code to wait for myAsyncTask to finish
}
The problem is how can I wait for myAsyncTask to finish before starting the new iteration. One possible solution I thought is to put a Handler inside to wait for some seconds. For example:
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
}
}, 5000);
But this is not right even if it is working, because you have to put a standard time to wait for myAsyncTask. And it isn't sure if myAsyncTask finishes early or late because the a,b.. parameters change in every iteration.
Another solution I thought is to ask every time in a loop the object aTask to get the Status of myAsyncTask and check if it is FINISHED. For example:
while(!aTask.getStatus().equalsTo(AsyncTask.Status.FINISHED)){
}
But this isn't working because myAsyncTask is always RUNNING.
Can somebody help me?

//Code to wait for myAsyncTask to finish
If you wait there, you would freeze the UI. What is the point of another thread if you plan on waiting?
My guess what you need is to make use of the optional parameters in the execute(Params...) method. You can put your 'a','b' or what ever those objects are and how many you put doesn't matter. This will allow your task to execute all of them and have better contorl.
protected Long doInBackground(Objects... objs){
for(int i = 1; i < objs.length; i++){
// Do Work
}
}

In Android you must not stay in the UI Thread and wait for something. This includes waiting for AsyncTasks that run in the background. If you would wait in the calling Thread, it would block the user and would freeze your device.
You must separate the code into two pieces.
First start the AsyncTasks:
for(int i=0; i<num; i++){
//a,b.. are changing
aTask = new myAsyncTask(a,b..);
aTask.execute();
// dont wait here. It would freeze the UI Thread
}
Second run code when Task has finished just overwrite the onPostExecute() method in MyAsyncTask.
Then Android can do its work while the AsyncTasks run.

a simple suggestion, depending on what your for loop is actually doing, is to use the loop to create a 'queue' of items you want processed. then, fire off an asynctask with the first object in the queue, and upon completion of that task, fire off the next object in the queue until the length of your queue is zero

The easiest way is to create a method from where you call asynTask.execute() and call that method from onPostExecute().
public class ParentClass extends Activity
{
int asyncCalls = 0;
......
public void callAsyncTaskAgain()
{
aTask = new myAsyncTask(a,b..);
aTask.execute();
asyncCalls++;
}
public class AnotherClass extends AsyncTask
{
protected Void onPostExecute(Void result)
{
....
if(asyncCalls < SOME_NUMBER)
callAsyncTaskAgain();
}
}
}
Hope this helps

Related

Android: Array Loop - next element only after first loop finish

I would like to initiate void function with Array element. Simple example:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.seven_minutes);
Excercise();
final String Array[] = {"e1","e2","e3"};
for (int i = 0; i < Array.length; i++ ){
jump(Array[i]);
}
}
private void jump (String i){
wait 30s;
}
How to make, that second [i] would be initiate only after jump (Array[i]) function is finished? Like after 30s of it?
Assuming that jump method executed in a different thread, you need to execute inside the UI thread (onCreate) a join() method, that allows a thread to wait the finish of another thread, until it can continue its execution.
However, this does not seems a good idea, since your UI thread will be blocked for too much time.
You should manage them asynchronously. There are a lot of ways you can do it. For example you can use [AsyncTask][1] that allows you to execute some code in background (inside doInBackground) and at the end of this code executed in background, will be executed some code in the UI thread (onPostExecute).
So, you can check your condition the first time, then call the AsyncTask, and in onPostExecute recheck the condition (Array.lenght < i), and if it is verified, you can relaunch the AsyncTask.

Android ProgressBar not updating as expected?

I am dynamically populating a TableLayout with rows using an AsyncTask and am not getting the expected ProgressBar behavior.
I initially set the max value of the ProgressBar to three times the number of items I'm processing to accommodate three operations. Here's my code:
onPreExecute:
progress.setMax(items.size() * 3); // We need to create the rows and add listeners to them
Async Class
public class TableLoader extends AsyncTask<Object, String, Boolean>{
#Override
protected Boolean doInBackground(Object... params) {
for (int i = 0; i < items.size(); i++) {
// doStuffToCreateRow
rows.add(row);
progress.publishProgress();
}
}
Then I have:
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (!isCancelled()) {
for (int i = 0; i < rows.size(); i++) {
table.addView(rows.get(i));
progress.incrementProgressBy(1);
}
table.requestLayout();
listener.onTaskCompleted();
}
}
Where publishProgress is simply:
protected void onProgressUpdate(String... values) {
progress.incrementProgressBy(1);
}
Finally, in my onPostExecute method, I have a callback to the activity that uses the AsyncTask, which looks like this in the main activity:
public void onTaskCompleted() { // TableRows have been generated
TableLayout itemTable = (TableLayout) findViewById(R.id.itemTable);
for (int i = 0; i < itemTable.getChildCount(); i++) {
TableRow row = (TableRow) itemTable.getChildAt(i);
// Create and add a listener to that row's checkbox
// progress.incrementProgressBy(1);
}
progress.setVisibility(View.GONE);
}
For some reason, the initial increment works properly and causes the progressBar to reach 1/3 full after its completion (generation of TableRows). Afterwards, there's a 2-5 second pause where the UI thread blocks and then the progressBar disappears, which I've set to occur after the for loop in the onTaskCompleted (the third and final operation) terminates. The data then shows up.
Why is my progressBar not updating as expected?
I've looked the issue up but have found people incrementing the bar by < 1 using division or not using the UI thread. To my knowledge, I'm updating using the UI thread and incrementing by 1/number of items * 3 each time.
For some reason, the initial increment works properly and causes the progressBar to reach 1/3 full after its completion
It means the Async part is working properly, your first third is the one involving doInBackground / ProgressUpdate, that is, heavy stuff is done outside of the UI thread and just signals progress update. The UI thread is sleeping and happily waiting for update requests.
Afterwards, there's a 2-5 second pause where the UI thread blocks and then the progressBar disappears, which I've set to occur after the for loop in the onTaskCompleted (the third and final operation) terminates. The data then shows up.
The other two operations take place in the UI thread. Both for in onPostExecuted and onTaskCompleted run on the UI Thread and are tight loops. The UI thread is blocked in your loops. So while you are inside those tight loops no matter how many thousand times you mingle with any UI component: setprogress, settext, etc.... It will not update until the for loop ends, your routine finishes, and the system takes control again of the UI thread.
What you can do is, as it looks like there's a big number of rows, to add them in chunks then update the progress. This will give some air. Take a look at the following prototype
Handler handler=new Handler();
interface AddRowListener {
public void onRowsAdded();
}
private void addRowChunk(final int startRow, final int totalRows, final AddRowListener listener) {
for (int i=startRow; i<startRow+CHUNK_SIZE; i++) {
if (i>=totalRows) {
// we have finished! Just call the listener
listener.onRowsAdded();
return;
}
addView....
}
// After adding some rows, we end here, and schedule a call to this function again
// to continue. The 25ms delay is optional, but it will give some air to Android for sure
handler.postDelayed(new Runnable() {
addRowChunk(startRow+CHUNK_SIZE, totalRows, listener);
}, 25);
}
private void addRows(AddRowListener listener) {
TableLayout itemTable = (TableLayout) findViewById(R.id.itemTable);
// we start the process, it will create row chunks and call the listener when done
addRowChunk (0, itemTable.getChildCount(), listener);
}
So to add the rows you now can do:
addRows (new AddRowListener() {
public void onRowsAdded() {
// the rows have been added.
}
});
By the way, what's your row size? it has to be huge !
PD_ You have to select a CHUNK_SIZE. Just experiment with values until the UI is snappy. I'd say 40-50 is OK, but it's just a wild guess and depends on the phone hardware.
Don't set your progress.setMax() inside doInBackground(), do it inside onPreExecute() instead. You should not modify the UI thread(Main Thread) from a worker/background thread, and there is exactly where doInBackground() runs. onPreExecute(), onProgressUpdate(), and onPostExecute() run on the UI Thread.
Why you are not using a thread to update the progress bar. This approach is perfect to update the progress........but if you want to set manually then first set the max for progress bar just call below method in your onPreExecute and then onPostExecute
progressBar.setProgress(value);
Where Value should be half of the max when you are calling from onPreExecute and then cent percent from onPostExecute......but I recommend you to use a thread to update the correct progress.
Do not use publishProgress in onPostExecute. Just call incrementProgressBy.

Android: Pause the thread for several seconds

ExecutorService exec = Executors.newFixedThreadPool(8);
List<Future<Object>> results = new ArrayList<Future<Object>>();
// submit tasks
for(int i = 0; i < 8; i++) {
results.add(exec.submit(new ThreadTask()));
}
...
// stop the pool from accepting new tasks
exec.shutdown();
// wait for results
for(Future<Object> result: results) {
Object obj = result.get();
}
class ThreadTask implements Callable<Object> {
public Object call() {
// execute download
//Inside this method I need to pause the thread for several seconds
...
return result;
}
}
As shown above in the comment I need to pause the thread for several seconds. Hope you can help me with this.
Thanks for your time!
Just call Thread.sleep(timeInMillis) - that will pause the current thread.
So:
Thread.sleep(5000); // Sleep for 5 seconds
Obviously you shouldn't do this from a UI thread, or your whole UI will freeze...
Note that this simple approach won't allow the thread to be woken up other by interrupting it. If you want to be able to wake it up early, you could use Object.wait() on a monitor which is accessible to whichever code needs to wake it up; that code could use Object.notify() to wake the waiting thread up. (Alternatively, use a higher-level abstraction such as Condition or Semaphore.)
you could implement a new thread, which is not the UI thread..
something like this might do it for you..
class ThreadTask implements Callable<Object> {
public Object call() {
Thread createdToWait= new Thread() {
public void run() {
//---some code
sleep(1000);//call this function to pause the execution of this thread
//---code to be executed after the pause
}
};
createdToWait.start();
return result;
}

Implementing a while loop in android

I can't understand the implementation of a while loop in android.
Whenever I implement a while loop inside the onCreate() bundle, (code shown below)
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView=(TextView)findViewById(R.id.TextView);
while (testByte == 0)
updateAuto();
}
nothing boots up, and the program enters a "hanging" state after a while and I can't understand why. Testbyte is as follows:
byte testByte == 0;
and updateAuto() is supposed to update the code per 1 second and display inside the textView portion. I've been using setText inside updateAuto() as shown below and everything works fine, but once i implement the while loop all i see is a black screen and then an option to force close after a few seconds due to it "not responding".
TextView.setText(updateWords);
I've changed it to a button format (meaning i have to click on the button to update itself for now), but i want it to update itself instead of manually clicking it.
Am i implementing the while loop in a wrong way?
I've also tried calling the while loop in a seperate function but it still gives me the black screen of nothingness.
I've been reading something about a Handler service... what does it do? Can the Handler service update my TextView in a safer or memory efficient way?
Many thanks if anyone would give some pointers on what i should do on this.
Brace yourself. And try to follow closely, this will be invaluable as a dev.
While loops really should only be implemented in a separate Thread. A separate thread is like a second process running in your app. The reason why it force closed is because you ran the loop in the UI thread, making the UI unable to do anything except for going through that loop. You have to place that loop into the second Thread so the UI Thread can be free to run. When threading, you can't update the GUI unless you are in the UI Thread. Here is how it would be done in this case.
First, you create a Runnable, which will contain the code that loops in it's run method. In that Runnable, you will have to make a second Runnable that posts to the UI thread. For example:
TextView myTextView = (TextView) findViewById(R.id.myTextView); //grab your tv
Runnable myRunnable = new Runnable() {
#Override
public void run() {
while (testByte == 0) {
Thread.sleep(1000); // Waits for 1 second (1000 milliseconds)
String updateWords = updateAuto(); // make updateAuto() return a string
myTextView.post(new Runnable() {
#Override
public void run() {
myTextView.setText(updateWords);
});
}
}
};
Next just create your thread using the Runnable and start it.
Thread myThread = new Thread(myRunnable);
myThread.start();
You should now see your app looping with no force closes.
You can create a new Thread for a while loop.
This code will create a new thread to wait for a boolean value to change its state.
private volatile boolean isClickable = false;
new Thread() {
#Override
public void run() {
super.run();
while (!isClickable) {
// boolean is still false, thread is still running
}
// do your stuff here after the loop is finished
}
}.start();

Check if all AsyncTasks have finished

I have 3 AsyncTasks and 1 ProgressBar. I want when any of task executes, the progress bar is visible and when all of them finish, the progress bar is invisible.
In Java, there is ExecutorService::isTerminated to check if all runnables finished but Android doesn't have it.
Update: 3 tasks execute at the same time.
Figure.
Nice graphic. But I am afraid there is no build in mechanism for this. You'll have to implement it by yourself. There are few solutions you could use -
Keep a reference to all 3 task. When task finishes check if the other two tasks are finished too, if yes than close the progress dialog if no wait for some other task to finish and check again. Make sure you free the references when you're done.
If you don't want to keep a reference store a counter. When the task finishes, increment the counter and check if it's equal to 3. If all tasks finished and you are done. If you implement this make sure to synchronized the access to the counter.
Try using AsyncTask.getStatus(). This works perfectly fine. Refer below sample code.
List<AsyncTask<String, String, String>> asyncTasks = new ArrayList<AsyncTask<String, String, String>>();
AsyncTask<String, String, String> asyncTask1 = new uploadTask().execute(string);
AsyncTask<String, String, String> asyncTask2 = new downloadTask().execute(string);
AsyncTask<String, String, String> asyncTask3 = new createTask().execute(string);
asyncTasks.add(asyncTask1);
asyncTasks.add(asyncTask2);
asyncTasks.add(asyncTask3);
You can later loop the AsyncTaskList and find each of the tasks' status as below.
for(int i=0;i<asyncTasks.size();i++){
AsyncTask<String, String, String> asyncTaskItem = (AsyncTask<String, String, String>)asyncTasks.get(i);
// getStatus() would return PENDING,RUNNING,FINISHED statuses
String status = asyncTaskItem.getStatus().toString();
//if status is FINISHED for all the 3 async tasks, hide the progressbar
}
A simple workaround would be to use three boolean variables one each for each AsyncTask and then check them accordingly.
A better approach would be to create a separate class that extends AsynTask and defines a callback interface which is fired in onPostExecute.
create a field to hold all tasks:
private ArrayList<HtmlDownloaderTask> mTasks;
Start your tasks this way:
HtmlDownloaderTask = new HtmlDownloaderTask(page.getHtml());
task.execute(page.getUrl());
//if you want parallel execution try this:
//task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,page.getUrl());
mTasks.add(task);
on the onPostExecute of MyAsyncTask:
int unfinishedTasks = 0;
for (HtmlDownloaderTask myDT : mTasks){
if(!(myDT.getStatus() == AsyncTask.Status.FINISHED)){
unfinishedTasks++;
}
}
if (unfinishedTasks == 1){
//We are all done. 1 Because its the current one that hasnt finished post execute
callWhateverMethod();
}
Well as you do know when an AsyncTask ends (when onPostExecute gets called):
one solution could be to create a method setProgressBarVisible() that keeps a counter and when first called sets visible, and a method setProgressBarInvisible() that decreases the counter and when zero sets the progress bar invisible.
:-? i think it's just a trick. you will return some message at onPostExecute of each Asyntask and compare it. (this message can contain a time, for example)
A official support of CompletableFuture was introduced since API level 24.
It's also available in Java 8 here.
Can use simply use something like:
taskA.thenCombine(taskB).thenCombine(taskC)
I would simply notify it at onPostExecute(), refer to onPostExecute and 4 steps in the document for detail and you can use EventBus to do some subscribe things.
This is a common question when you want to run a bunch of AsynTasks on a THREAD_POOL_EXECUTOR. It's much more faster than if you just call .execute() and all your tasks are done one by one.
So if you have multiple jobs and objects are not depending on each other states - try to run on a thread pool.
But the question is: how do I know that all of my tasks are done?
There is no built in methods in AsyncTask so you should do a little workaround.
In my case I added a static Hashmap field to my Asynctask class to keep track of all started and finished tasks. As a bonus of a map I can always know which task is currently in progress.
private static HashMap<Uri, Boolean> mapOfAttachmentTasks = new HashMap<>();
and ad simple three methods to access this map.
Important: they should be synchronized
public static synchronized void addTask(Uri uri){
mapOfAttachmentTasks.put(uri, true);
}
public static synchronized void removeTask(Uri uri){
mapOfAttachmentTasks.remove(uri);
}
public static synchronized boolean isTasksEmpty(){
return mapOfAttachmentTasks.isEmpty();
}
You want to add a new item to the tracking Map in an AsyncTask constructor and remove it in onPostExecute():
public AttachmentTask(Uri uri) {
this.uri = uri;
addTask(uri);
}
#Override
protected void onPostExecute(Attachment attachment) {
removeTask(uri);
if(isTasksEmpty())
EventBus.getDefault().post(new AttachmentsTaskFinishedEvent(attachment));
}
Everytime a task is finished it calls onPostEexecute and you check if it was the last task. If there is no tasks left - send a signal that you're done.
Now, here I used EventBus to send event to my Fragment but you can use a callback. In this case you should create an interface with callbackMethod, your Fragment (any of your UI components which are waiting for the event) should implement this interface and have that method. Then in AsyncTask constructor you get your Fragment as an argument and keep a reference to it, so you can call it's callback method when everything is done.
But I dont like such approach. First you need to keep the reference of your Fragment (or any other UI) in a WeakReference wrapper becasue you will get a memory leak when your fragment is dead (but still kept in memory becasue your AsyncTask has it's reference).
Also you would need to make a lot of checks and it will look something like that:
private boolean isAlive() {
return mFragmentWeakReference != null
&& mFragmentWeakReference.get() != null
&& mFragmentWeakReference.get().isAdded()
&& mFragmentWeakReference.get().getActivity() != null
&& !mFragmentWeakReference.get().getActivity().isFinishing();
yep, in production you should be a little paranoic and do all these checks :)
That's why you can use EventBus and if your UI is dead - whatever.
try this, maybe can help you...
final ImageUploader _upload = new ImageUploader();
_upload.setValue(getApplicationContext(), _imagepath, _urlPHP);
_upload.execute();
Runnable _run;
Handler _h2;
_run = new Runnable() {
public void run() {
_h2 = new Handler();
_h2.postDelayed(this, 1000);
Toast.makeText(getApplicationContext(), "not finished", Toast.LENGTH_LONG).show();
if (_upload.getStatus() == AsyncTask.Status.FINISHED) {
Toast.makeText(getApplicationContext(), "finished", Toast.LENGTH_LONG).show();
_h2.removeCallbacks(_run);
}
}
};
_h2 = new Handler();
_h2.postDelayed(_run, 1);

Categories

Resources