AsyncTask Not Updating on UI thread - android

I have an AsyncTask updating an ActionBarSherlock progress implementation. Somehow the onProgressUpdate is throwing a threading error though it claims to execute on the UI thread.
protected void onProgressUpdate(Integer... values)
{
setSupportProgress(values[0]);
}
The error is:
03-06 00:13:11.672: E/AndroidRuntime(4183): at com.anthonymandra.framework.GalleryActivity$ShareTask.onProgressUpdate(GalleryActivity.java:476)
Only the original thread that created a view hierarchy can touch its
views.
As far as I can tell I should be accessing the UI thread for this...
I have many working AsyncTasks in my app, but as requested here's the doInBackground (simplified):
for (MediaObject image : toShare)
{
BufferedInputStream imageData = image.getThumbStream();
File swapFile = getSwapFile(...);
write(swapFile, imageData);
++completed;
float progress = (float) completed / toShare.size();
int progressLocation = (int) Math.ceil(progressRange * progress);
onProgressUpdate(progressLocation);
}

Okay so the problem is you are calling onProgressUpdate when you should call publishProgress. The OP figured out this himself/herself so I just copy pasted it so he/she does not need to wait to accept the answer. Below is information how AsyncTasks works and it is good knowledge.
Are you creating the AsyncTask on the UI thread? If you are not that is the problem. onProgressUpdate will be run on the thread that created the AsyncTask.
Update: Let us have some code digging time (API 15 source code)!
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
This fellow will call it's static Handler sHandler. The documentation says:
When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
Thanks to Bruno Mateus with his documentation look-up skills:
Look that, i found at documentation page: Threading rules - There are
a few threading rules that must be followed for this class to work
properly: - The AsyncTask class must be loaded on the UI thread. This
is done automatically as of JELLY_BEAN. - The task instance must be
created on the UI thread. execute(Params...) must be invoked on the UI
thread. - Do not call onPreExecute(), onPostExecute(Result),
doInBackground(Params...), onProgressUpdate(Progress...) manually. -
The task can be executed only once (an exception will be thrown if a
second execution is attempted.)

You can declare your AsyncTask as a innerclass of your activity like that:
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}

I was manually calling:
onProgressUpdate
You should call
publishProgress
Easy mistake, but great info. Simon and Bruno deserve the credit, post the answer if you like. Thanks for the fast and extensive response!

Related

Android Activity UI Performance (AsyncTask while-loop)

How can I make this more efficient, at the moment it hangs the activity, giving the following information in logcat every time the while-loop completes I assume:
I/Choreographer: Skipped 55 frames! The application may be doing too much work on its
main thread.
Basically on a while-loop it reads a string variable, modifies and splits the string into parts which are then multiplied then divided, these final values are picked up by an interface which changes the custom UI element in the activity.
This seems too heavy on the main UI thread, I was under the impression that handler.post alleviates some of this by adding to the message queue however there are skipped frames.
I've tried to convert this code into an AsyncTask however I don't understand how this code can be converted to work in AsyncTask.
EDIT: AsyncTask custom class replacing old Thread while-loop.
(Old code for reference: http://pastebin.com/Dek6uQTE)
I'm still unsure how this fits in with AsyncTask, I have added the heavy code within the doInBackground() method however readBuf.replace and readBuf.split cannot be resolved. I thought to put the end changed in onProgressUpdate() as opposed to onPostExecute() as this would keep the UI elements updated automatically.
private class PostTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
readBuf = ((MyApplication) getApplication()).getReadBuf();
}
#Override
protected Void doInBackground(Void... readBuf) {
while (readBuf.length > 4) {
readBuf.replace("V", "");
String[] parts = readBuf.split(",");
String part1 = parts[0];
String part2 = parts[1];
speed1 = Float.parseFloat(part1);
speed2 = Float.parseFloat(part2);
finalspeed1 = (speed1 * 102) / div1;
finalspeed2 = (speed2 * 602) / div1;
publishProgress();
}
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
speedometer.onSpeedChanged(speedometer.getCurrentSpeed() - speedcur1);
speedometer.onSpeedChanged(speedometer.getCurrentSpeed() + finalspeed1);
speedometer1.onSpeedChanged(speedometer.getCurrentSpeed() - speedcur2);
speedometer1.onSpeedChanged(speedometer1.getCurrentSpeed() + finalspeed2);
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
You're starting a new thread which is actually just posting a new Runnable to the UI thread (handler is created on the UI thread), so it's not being done in the background. Here are a few tips to help with this:
If you are going to use a background thread to do the "work", then don't post a Runnable which is performing work to the handler, just post the result and update your UI accordingly.
Don't call findViewById() in a loop or any time after creation, if possible. Get a reference to the UI element and stash it off. This method is expensive as it does a search through your view hierarchy for the matching ID.
If you are going to use an AsyncTask just move the "work" part of your Runnable to the doInBackground() method and your UI updates into the onPostExecute() method.
Whether using a custom background Thread or AsyncTask, be sure to shutdown/cancel the work when your Activity leaves the run state, otherwise you will encounter problems as these components are not lifecycle aware.
It's not clear where readBuf is actually pulling data from, so this may also need some work. If this is real-time data coming from some other source, then you may need to have the custom Thread loop with a small yield. If using an AsyncTask, you'll have to create a new one each time as they are one-shot operations and not intended to be used as a long running background thread.
This article on AsyncTask has more details about how it works and pitfalls.

Android AsyncTask: what is difference between execute() and get()?

Should I write smth like that
return task.exec(session, state).get(json_timeout, TimeUnit.MILLISECONDS);
Or I can do like this
task.exec(session, state, result);
return result;
A have already read all documentation that I found, but failed to find an answer. My bad...
Do not use get(). It will block the ui thread until asynctask finishes execution which no longer makes it asynchronous.
Use execute and to invoke asynctask
new task().exec(session, state, result);
Also you can pass the params to the constructor of asynctask or to doInbackground()
http://developer.android.com/reference/android/os/AsyncTask.html
public final Result get ()
Added in API level 3
Waits if necessary for the computation to complete, and then retrieves its result.
You can make your asynctask an inner class of your activity class and update ui in onPostExecute.
If asynctask is in a different file then you can use interface.
How do I return a boolean from AsyncTask?
AsyncTask#get() will block the calling thread.
AsyncTask#execute() will run in a separate thread and deliver the Result in onPostExecute(...).
I would recommend against using the get() method except in special cases like testing. The whole purpose of the AsyncTask is to execute some long-running operation in doInBackground() then handle the result once it's finished.
One example of normal AsyncTask execution would look like:
Task task = new Task(){
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
//handle your result here
}
};
task.execute();

AsyncTask not running asynchronously

The following were supposed to be the same if I am not mistaking.
Using AsyncTask:
private class GetDataTask extends AsyncTask<String, Void, String>{
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(String... params) {
return NetConnection.getRecordData(mUserId, mUserPassword);
}
#Override
protected void onPostExecute(String result) {
parseJson(result);
}
}
Using a Thread:
new Thread( new Runnable() {
#Override
public void run() {
String res = NetConnection. getRecordData(mUserId, mUserPassword);
parseJson(res);
}
}).start();
But when uploading a file, the AsyncTask runs synchronously while the Thread run asynchronously(in parallel).
Why is so? Why AsyncTask behaves like this? Isn't AsyncTask supposed to run asynchronously?
I am little confused so I need your help.
This is how I invoke the GetDataTask:
new GetDataTask().execute()
I prefer using AsyncTask but it is not doing the job for me. Please refer to my early question for more details
As of 4.x calling 2 AsyncTasks will cause them to be executed serially.
One way to fix this is using the following code
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else {
myTask.execute();
}
You can read more at: http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html
Ok following are the notes from the official java doc...
Order of execution
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.
SO if you invoke two AsyncTask together.. they would not be executed in parallel (exception is donut, encliar and gingerbread)... You can use executeOnExecutor to execute them in parallel...
From your code we can see you have called parseJson(result); in onPostExecute() of AsyncTask which runs in MainUIThread of Applications. So at that point your code runs Synchronously..
Put parseJson(result); method in doInBackGround() Which runs only in other worker thread.
While you have called same thing in Thread. So both
String res = NetConnection. getRecordData(mUserId, mUserPassword);
parseJson(res);
Runs in other worker thread out of MAinUiThread on which you experienced Asynchronously.
Note:
But be sure your parseJson(res); doesn't update UI while it is in doInBackground().
Looks like that actual problem is not in file uploading but in parseJson method.
In your Thread example you parsing Json in separate thread while in AsyncTask case you parsing Json in UI thread.

Android: got CalledFromWrongThreadException in onPostExecute() - How could it be?

I have an app in production for a few weeks, using ACRA, and I had zero errors until one strange error reported today.
I've got:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
coming from this method in the stack trace (retraced):
at my.app.CountdownFragment$1.void onPostExecute(java.lang.Object)(SourceFile:1)
And this is the relevant source snippet:
private void addInstructionsIfNeeded() {
if (S.sDisplayAssist) {
new AsyncTask<String, Void, String>() {
#Override
protected String doInBackground(String... params) {
return null;
}
/*
* runs on the ui thread
*/
protected void onPostExecute(String result) {
Activity a = getActivity();
if (S.sHelpEnabled && a != null) {
in = new InstructionsView(a.getApplicationContext());
RelativeLayout mv = (RelativeLayout) a
.findViewById(R.id.main_place);
mv.addView(in.prepareView());
}
};
}.execute("");
}
}
Where addInstructionsIfNeeded() is called from a handler dispatched message (the UI thead).
onPostExecute() runs on the UI thread, so why I've got "wrong thread"?
This code ran already on more than 150 devices, and more than 100000 times (according to Flurry), and never had this error.
The originating device is Samsung SGH-I997 running SDK 4.0.4
My question is: How could it be?
EDIT:
This all happens in a fragment
i was suffering from the same problem, this is another android framework bug...
what is happening:
in certain circumstances an application can have more than one "looper" and therefore more than one "UI thread"
--side note-- i am using the term "UI thread" in the loosest of senses in this answer, since when people say "UI thread" they usually mean main or entry thread, Android like many of other OS before it, allow for for multiple message pumps (called a Looper in Android, see: http://en.wikipedia.org/wiki/Event_loop) for different UI trees, as such android for all intents and purposes is capable of running more than one "UI thread" in certain circumstances and using that term leads to rampant ambiguities... --end side note--
this means:
since an application can have more than one "UI thread" and an AsyncTask always "Runs on the UI thread" [ref], someone decided [poorly] that instead of the AsyncTask always running on its creation thread (which in 99.999999% of cases would be the correct "UI thread") they decided to use hocus pocus (or a poorly crafted shortcut, you decide) to execute on the "main looper"..
example:
Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId());
Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId());
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
Log.i("doInBackground ran ON: " + Thread.currentThread().getId());
// I'm in the background, all is normal
handler.post(new Runnable() {
#Override
public void run() {
Log.i("Handler posted runnable ON: " + Thread.currentThread().getId());
// this is the correct thread, that onPostExecute should be on
}
});
return null;
}
#Override
protected void onPostExecute(Void result) {
Log.i("onPostExecute ran ON: " + Thread.currentThread().getId());
// this CAN be the wrong thread in certain situations
}
}.execute();
if called from the bad situation described above the output will look something like this:
AsyncTask / Handler created ON: 16
Main Looper: 1 myLooper: 16
doInBackground ran ON: 12
onPostExecute ran ON: 1
Handler posted runnable ON: 16
that's a huge FAIL for AsyncTask
as shown this can be mitigated using a Handler.post(Runnable) in my specific case the duality of my "UI thread" situation was caused by the fact that I was creating a dialog in response to a JavaScript interface method called from a WebView, basically: the WebView had its own "UI thread" and that was the one that i was currently running on..
from what i can tell (without really caring about or reading into it too much) it seems that the AsyncTask class' callback methods in general run off a single statically instantiated handler (see: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/os/AsyncTask.java#AsyncTask.0sHandler), which means that it is always going to execute on the "main thread" or "entry thread" which they incorrectly refer to as the "UI thread" (which is presumed as any thread where UI interactions take place, eg. multiple threads in this case) this is both shoddy craftsmanship and shoddy documentation from the android team... weak sauce, the sauce is weak
hope this helps you -ck
Had the same issue. Solved in my case
Briefly explanation:
Running AsynckTask for the very first time on non UI thread with looper leads to loading AsyncTask.class and initialization sHandler to handler constructed on that non UI looper.
Now sHandler is connected to that non UI thread for ANY instance of AsyncTask subclasses and onPreExecute, onProgressUpdate and onPostExecute methods will be invoked on that non UI thread (unless AsyncTask.class will be unloaded)
Any attempt to deal with UI inside any of the above methods will lead to crash with android.view.ViewRootImpl$CalledFromWrongThreadException
To avoid such situation one should always run (at least for the very first time) AsyncTask on UI thread in order to let AsyncTask's sHandler-field be initialized with UI's looper
The story:
There were two production apps: A - main android app and B - some utilty app.
After integration app B ito app A we received a lot of crashes:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
for method running from AsynckTask.onPostExecute()
After some investigation it appeared that utility app B used AsyncTask within its HandlerThread
The traces was found in AsyncTask's source code:
private static final InternalHandler sHandler = new InternalHandler();
This is the handler which is used to send onPostExecute() to UI thread.
This handler is static and it will be initialized during class loading i.e. first new AsyncTask() appearance
It means that onPostExecute will always be posted to that thread where new AsyncTask() was called for the first time (unless AsyncTask.class will be unloaded and loaded again)
In my case the flow was something like this:
1 - starting app A
2 - initializing B form A
3 - B creates its own HandlerThread and launches AsyncTask <- now onPostExecute wil be posted to this HandlerThread no matter where from an instance of AsyncTask will be launched in future
4 - create AsyncTask in the app A for a long operation and update UI in its onPostExecute
5 - when executing onPostExecute() the CalledFromWrongThreadException is thrown
Then a friend of mine showed me related documentation from android.developers (Threading rules section):
The AsyncTask class must be loaded on the UI thread. This is done
automatically as of JELLY_BEAN. The task instance must be created on
the UI thread. execute(Params...) must be invoked on the UI thread.
Hope it can help to make clear the situation)
Maybe the reason is Flurry?
I had this exception when I used Flurry 3.2.1. But when I went back to Flurry 3.2.0 I didn't have this exception
Use Flurry 3.2.2 and above.
Placing the following line of code in the Application onCreate should solve the problem:
/**
* Fixing AsyncTask Issue not called on main thread
*/
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
It seems the issue is created when the AsyncTask class is first initiated on a different main Thread which is not our main Thread, I checked it by adding the code in the bottom, to my Application onCreate
new Thread(new Runnable() {
#Override
public void run() {
Log.i("tag","1.3onPostExecute ran ON: " + Thread.currentThread().getId());
Looper.prepare();
new AsyncTask<Void,Void,Void>(){
#Override
protected Void doInBackground(Void... params) {
Log.i("tag","2onPostExecute ran ON: " + Thread.currentThread().getId());
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
Log.i("tag","1.2onPostExecute ran ON: " + Thread.currentThread().getId());
super.onPostExecute(aVoid);
}
}.execute();
Looper.loop();
Looper.myLooper().quit();
}
}).start();
This code will init the AsynTask in a main Thread which is not the application main, and will cause the application to crash in any other AsyncTask which will do any UI on the post-execute. crashing with the CalledFromWrongThreadException
Hope it cleared things a little bit more.
Thanks all for the great help on this.
Where is
runOnUiThread(new Runnable() {
public void run() { /*code*/ } );
in your code
/*
* runs on the ui thread
*/
protected void onPostExecute(String result) {
Activity a = getActivity();
if (S.sHelpEnabled && a != null) {
in = new InstructionsView(a.getApplicationContext());
runOnUiThread(new Runnable() {
public void run() {
RelativeLayout mv = (RelativeLayout) a
.findViewById(R.id.main_place);
mv.addView(in.prepareView());
}
}
};
Try this code. I think this would fix the problem
I think the problem lies in the line Activity a = getActivity(); I think you should do that before going into the AsyncTask

Update UI from Thread in Android

I want to update my UI from a Thread which updates a Progressbar. Unfortunately, when updating the progressbar's drawable from the "runnable" the progressbar disappears!
Changing the progressbars's drawable in onCreate() on the otherside works!
Any Suggestions?
public void onCreate(Bundle savedInstanceState) {
res = getResources();
super.onCreate(savedInstanceState);
setContentView(R.layout.gameone);
pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); //**Works**/
handler.postDelayed(runnable, 1);
}
private Runnable runnable = new Runnable() {
public void run() {
runOnUiThread(new Runnable() {
public void run()
{
//* The Complete ProgressBar does not appear**/
pB.setProgressDrawable(getResources().getDrawable(R.drawable.green));
}
});
}
}
You should do this with the help of AsyncTask (an intelligent backround thread) and ProgressDialog
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called begin, doInBackground, processProgress and end.
The 4 steps
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread immediately after the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
Threading rules
There are a few threading rules that must be followed for this class to work properly:
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
Example code
What the adapter does in this example is not important, more important to understand that you need to use AsyncTask to display a dialog for the progress.
private class PrepareAdapter1 extends AsyncTask<Void,Void,ContactsListCursorAdapter > {
ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(viewContacts.this);
dialog.setMessage(getString(R.string.please_wait_while_loading));
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
}
/* (non-Javadoc)
* #see android.os.AsyncTask#doInBackground(Params[])
*/
#Override
protected ContactsListCursorAdapter doInBackground(Void... params) {
cur1 = objItem.getContacts();
startManagingCursor(cur1);
adapter1 = new ContactsListCursorAdapter (viewContacts.this,
R.layout.contact_for_listitem, cur1, new String[] {}, new int[] {});
return adapter1;
}
protected void onPostExecute(ContactsListCursorAdapter result) {
list.setAdapter(result);
dialog.dismiss();
}
}
The most simplest solution I have seen to supply a short
execution to the UI thread is via the post() method of a view.
This is needed since UI methods are not re-entrant. The
method for this is:
package android.view;
public class View;
public boolean post(Runnable action);
The post() method corresponds to the SwingUtilities.invokeLater().
Unfortunately I didn't find something simple that corresponds to
the SwingUtilities.invokeAndWait(), but one can build the later
based on the former with a monitor and a flag.
So what you save by this is creating a handler. You simply need
to find your view and then post on it. You can find your view via
findViewById() if you tend to work with id-ed resources. The resulting
code is very simple:
/* inside your non-UI thread */
view.post(new Runnable() {
public void run() {
/* the desired UI update */
}
});
}
Note: Compared to SwingUtilities.invokeLater() the method
View.post() does return a boolean, indicating whether the
view has an associated event queue. Since I used the
invokeLater() resp. post() anyway only for fire and forget,
I did not check the result value. Basically you should
call post() only after onAttachedToWindow() has been called
on the view.
Best Regards
If you use Handler (I see you do and hopefully you created its instance on the UI thread), then don't use runOnUiThread() inside of your runnable. runOnUiThread() is used when you do smth from a non-UI thread, however Handler will already execute your runnable on UI thread.
Try to do smth like this:
private Handler mHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gameone);
res = getResources();
// pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); **//Works**
mHandler.postDelayed(runnable, 1);
}
private Runnable runnable = new Runnable() {
public void run() {
pB.setProgressDrawable(getResources().getDrawable(R.drawable.green));
pB.invalidate(); // maybe this will even not needed - try to comment out
}
};
Use the AsyncTask class (instead of Runnable). It has a method called onProgressUpdate which can affect the UI (it's invoked in the UI thread).
You need to create a Handler in the UI thread and then use it to post or send a message from your other thread to update the UI
If you don't like the AsyncTask you could use the observer pattern. In that example use the ResponseHandler as an inner class in your activity then have a string message that will set the progress bars percentage... You would need to make sure that any alterations to the UI are performed within the ResponseHandler to avoid freezing up the UI, then your worker thread (EventSource in the example) can perform the tasks required.
I would use the AsyncTask tho, however the observer pattern can be good for customization reasons, plus its easier to understand. Also im not sure if this way is widely accepted or will 100% work. Im downloading and the android plugin now to test it
As recommended by official documentation, you can use AsyncTask to handle work items shorter than 5ms in duration. If your task take more time, lookout for other alternatives.
HandlerThread is one alternative to Thread or AsyncTask. If you need to update UI from HandlerThread, post a message on UI Thread Looper and UI Thread Handler can handle UI updates.
Example code:
Android: Toast in a thread

Categories

Resources