Updating progressbar in AsyncTask doInBackground()? - android

One of the first things I learned when beginning android development (and beginners development in general) is that the UI should not (and in many cases can not) be updated by any other thread besides the main UI thread.
I have an AsyncTask that is moving a bunch of files in its doInBackground(). I have a progressbar that represents that to the user.
For the heck of it (mostly due to laziness) I decided to try the progressBar.setProgress([updated progress]) right in the doInBackground() method/thread, and to my surprise, it works fine.
However, that seems to go against the convention I've learned. I know AsyncTask has a onProgressUpdate() or whatever, but it's confusing me a bit, and I'm not sure if it's worth switching, since the current implementation seems to be working fine.
Should I not be updating the progressbar in this background thread?

ProgressBar is able to do this without crashing because setProgress() calls refreshProgress() (a Thread-safe method), when it is time to actually refresh the View.
private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
if (mUiThreadId == Thread.currentThread().getId()) {
doRefreshProgress(id, progress, fromUser, true);
} else {
if (mRefreshProgressRunnable == null) {
mRefreshProgressRunnable = new RefreshProgressRunnable();
}
final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
mRefreshData.add(rd);
if (mAttached && !mRefreshIsPosted) {
post(mRefreshProgressRunnable);
mRefreshIsPosted = true;
}
}
}
It posts a Runnable to the UI Thread if the method call was made a non-UI Thread, avoiding CalledFromWrongThreadExceptions.
However, always use the UI thread for UI related calls; the Android developers can change the implementation anytime and Views are usually not Thread-safe.

You should not modify the ProgressBar and/or call any of its methods directly from inside doInBackground(), as the Android UI toolkit is not thread safe.
Instead, call publishProgress() from inside doInBackground() and you will receive a subsequent callback on the UI thread in onProgressUpdate() where you can modify the ProgressBar however you like.

Related

how to run a algorithm on a separate thread while updating a progressBar

I have an android linear search algorithm for finding duplicate files and packed it in the function
public void startSearch()
I was able to run it in a separate thread like this
class ThreadTest extends Thread {
public void run() {
startSearch()
}
}
but when i try to update the progressbar in that thread,it throws a exeption and says i the ui thread can only touch it's views
is there any other way to do this?
There are so many ways to do it, some of them are deprecated, some add unnecessary complexitiy to you app. I'm gonna give you few simple options that i like the most:
Build a new thread or thread pool, execute the heavy work and update the UI with a handler for the main looper:
Executors.newSingleThreadExecutor().execute(() -> {
//Long running operation
new Handler(Looper.getMainLooper()).post(() -> {
//Update ui on the main thread
});
});
Post the result to a MutableLiveData and observe it on the main thread:
MutableLiveData<Double> progressLiveData = new MutableLiveData<>();
progressLiveData.observe(this, progress -> {
//update ui with result
});
Executors.newSingleThreadExecutor().execute(() -> {
//Long running operation
progressLiveData.postValue(progress);
});
Import the WorkManager library build a worker for your process and observe the live data result on the main thread: https://developer.android.com/topic/libraries/architecture/workmanager/how-to/intermediate-progress#java
Complex can have different interpretations. The best way is to have Kotlin Courtines, RxJava with dispatchers.What you have mentioned is a way but if you have multiple threads dependent on each other, then thread management becomes trickier. On professional apps, you would want to avoid the method that you have mentioned because of scalability in future.

Should a game loop be placed inside an AsyncTask

I am making an android game which is made up of a game loop that is constantly running as well as use of the android UI stuff. So they need to be in separate threads to work concurrently.
I am planning to put the game loop inside an AsyncTask however it says in the developer.android documentation that
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
My game loop will in theory be operating indefinitely and almost always for more than a few seconds. Is the asynctask the right place to put this then or is there another preferred method to split up a game loop and the UI operations
AsyncTasks are for short operations only, as the documentation has stated. Also, they usually do some work that shouldn't interfere with the UI. Hence, "Async" and "Task".
What you should use instead is a new Thread. This is called Multi-Threading. There are a lot of Game Frameworks out there which will have problems with android's UI. Though you have not stated what UI Operations you are talking about, but if you plan to use the android's widgets(UI), you could call Activity.runOnUiThread() to run a code on the UI Thread, for example, changing a TextView's text.
Here is a snippet on how you would create a never ending loop in a new thread(or something like this, i dont remember if the function is private):
new Thread(new Runnable() {
#Override
private void run() {
while(true) {
//your code goes here
}
}
});
Although AsyncTask allows you to perform background operations and publish results on the UI thread without having to manipulate threads, it should ideally be used for short operations (a few seconds at the most).
To keep things simple, you could use a Handler, or even better, research about the various APIs provided by the java.util.concurrent package.
import android.os.Handler;
// Create the Handler
private Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnable = new Runnable() {
#Override
public void run() {
// Insert simulation processing code here
// Repeat 60 times per second
handler.postDelayed(this, 1000 / 60);
}
};
// Start the Runnable immediately
handler.post(runnable);
Remember that multi-threading is the easy part. Correct synchronization is hard.

Multiple aynchronous network calls blocking main thread because their output at same time?

I am using priority job queue , there are number of jobs running in parallel, so that their result populates on UI at same time which takes application to ANR, is there any way , so that i can run asynchronous calls and populate ui synchronously?
UI is always populated synchronously, if it is done in correct way. The correct way is to call activity.runOnUiThread(Runnable), directly or indirectly. Seems that your problem is that your jobs post to UI thread in a too high rate.
First, check if the Runnables to update UI does only UI work. Any calculations should be done outside the UI thread. If it is so, create an intermediate object which makes pauses between UI updates from the parallel jobs and so lets the UI thread to respond to updates from user. It can look as follows:
public class PauseMaker {
Semaphore sem = new Semaphore(1);
public void runOnUiThread(Runnable r) {
sem.aquire();
Thread.sleep(1);
activity.runOnUiThread(new Runnable(){
try {
r();
} finally {
sem.release();
}
});
}
}
You can use the zip operator of rxjava2 to merge the responses together and when the combined response comes you can populate the UI synchronously .. for reference you can check..
http://www.codexpedia.com/android/rxjava-2-zip-operator-example-in-android/
Note The zipper will the return merged response after all the responses are received

How can I start two asynchronous threads which join to common execution once both are complete?

I'm looking for a design pattern or approach for the following scenario. I wish to kick off two separate background threads for data retrieval from different sources. I then want one method (on the UI thread) to be called once both background threads have completed their work. As the data from the two sources must be combined to be useful, I must wait until both have finished retrieving before manipulating the data. How can I achieve this on the Android platform?
Edit: My first version has been bothering me, and I didn't like the necessary added boolean with it, so here's another version. Call it with this from onPostExecute of each added task.
ArrayList<AsyncTask> tasks;
public void doStuffWhenDone(AsyncTask finishedTask)
{
tasks.remove(finishedTask);
if(tasks.size() > 0)
return;
... do stuff
}
I'll keep the older one up also, since they both work, but I think the above is much cleaner. Now to go tidy up one of my earlier projects.
ArrayList<AsyncTask> tasks;
boolean hasBeenDone = false;
public void doStuffWhenDone()
{
for(int i=0;i<tasks.size();i++)
if(hasBeenDone || (tasks.get(i).getStatus() != AsyncTask.Status.FINISHED))
return;
hasBeenDone = true;
... do stuff
}
It's easily extendable to however many tasks you have, and there's no need for a thread to handle the threads. Just call the method at the end of each task. If it's not the last one done, nothing happens.
Edit: Good point, but I don't think it needs to be atomic. Since both AsyncTasks' onPostExecute methods run on the UI thread, they'll be called one after the other.
Use a CountDownLatch, like this:
CountDownLatch barrier = new CountDownLatch(2); // init with count=2
startWorkerThread1(barrier);
startWorkerThread2(barrier);
barrier.await(); // it will wait here until the count is zero
doStuffWithTheResult();
when a worker thread finishes, call barrier.countDown() from it.
You can use AsyncTask and an int to know if both jobs are finished...

Android Context Passing on doInBackground

I sent "Scores Activity" to doinbackground then run a function on Scores Activity but getting
"Only the original thread that created a view hierarchy can touch its views." on "birinci.setText(txt);" line.
what am I missing here looks using same context?
Scores Activity
{
Object[] stuff = {this.dhn, Scores.this};
ConnectXML runXML = new ConnectXML();
runXML.execute(stuff);
}
public void setScoreListUpdate(String txt)
{
birinci.setText(txt);
}
private Scores myScores;
protected String doInBackground(Object... arguments) {
myScores = (Scores)stuff[1];
myScores.setScoreListUpdate(result);
}
The error message already gives the answer: you can't touch (edit/modify/update/etc.) any views from a thread that did not create them. Since anything that is executed in the doInBackgrund(...) of an AsyncTask is done by a separate thread, you can't do any direct view manipulations in there.
The solution is quite simple: override the other methods an AsyncTask provides, depending on your needs. If you're trying to update a view after all work is done, simply override onPostExecute(...). If you want to indicate some sort of progress while the work is being done in the background, use onProgressUpdate(...). Everything in there is being executed by the main UI thread (which created all views).
Please have read through the documentation on AsyncTask, since that describes the different steps and possibilities quite clearly.

Categories

Resources