I'm working for an Android app and implementing a ProgressBar by using AsyncTask class.
The problem is that on some devices, it causes "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views." in onPostExecute. On those devices, the problem occurs 100%. On other devices, it works fine.
public final class MyAsyncTask extends AsyncTask<String, Integer, String>
{
private ProgressBar progress;
private ListActivity activity;
public MyAsyncTask(ListActivity activity, ProgressBar progress)
{
this.progress = progress;
this.activity = activity;
}
protected void onPreExecute()
{
this.progress.setVisibility(view.VISIBLE);
}
protected String doInBackground(String[] arg0)
{
// getting xml via httpClient
return string;
}
protected void onPostExecute(String result)
{
this.progress.setVisibility(view.GONE);
}
I don't understand why onPostExecute does not run on the UI thread, on those certain devices.
Next, I tried to call it with runOnUiThread, to make absolutely sure that it runs on the UI thread.
runOnUiThread(new Runnable() {
#Override
public void run() {
ProgressBar progress = (ProgressBar)findViewById(R.id.some_view_progressbar);
MyAsyncTask task = new MyAsyncTask(activity, progress);
task.execute();
}
} );
Even this did not solve the problem. The same exception still occurs.
From Log, I confirmed that Thread.currentThread().getId() is certainly different from the app's main activity's thread inside the handler.
I'm stuck. Any advice will be appreciated.
NOTE:I edited the sample code (not a real code) above to fix the wrong method name and missing "return string".
I will add more information later.
I don't see anything wrong with MyAsyncTask itself, but there are still other things that can go wrong.
Starting the AsyncTask
From the Android Docs
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 don't show where you normally instantiate, and execute the task, so make sure that you do this in code that's already on the UI/main thread. Note that the first bullet point above might explain why this works for you on some devices, and not on others.
Creating the View Hierarchy
The message tells you
Only the original thread that created a view hierarchy can touch its views.
and you're assuming that this is because your async task is (strangely) trying to modify the UI on a background thread. However, it is possible that you get this error because the async task modifies the UI on the main thread, but the UI (ProgressBar) was not created correctly in the first place.
See this question for an example of how you can erroneously create the view on the wrong thread (anything other than the main thread), and get this same error.
More
I would, however, like to see exactly where you are logging the thread ID, and what value(s) you're getting. If you check out my first two suggestions, and they don't solve your problem, then we may need more information.
You also mention a Handler (?), but don't show how or where you use that. Normally, using AsyncTask removes the need to use Handler, so I'm a little worried about how you might be using that.
Update
Per the discussion in comments below, it looks like the issue here is the one discussed in this question. Some code, probably running on a background thread, is first to cause the AsyncTask class to be loaded. The original (pre-Jelly Bean) implementation of AsyncTask required class loading to occur on the main thread (as mentioned in the Threading Rules above). The simple workaround is to add code on the main thread (e.g. in Application#onCreate()) that forces early, deterministic class loading of AsyncTask:
Class.forName("android.os.AsyncTask");
Make sure you are invoking aysnctask.execute() from the main thread only.
Write a handler in UI thread and call the handler from onPostExecute. It will solve the problem.
Something like this. Have a handler in UI thread (main thread):
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
//run on UI Thread
}
};
and call in onPostExecute() like this:
handler.sendEmptyMessage(MSG);
Related
I have a method invoked by onClickListener
#Override
public Object getData() {
Thread t = new Thread(new testThread());
t.start();
return false;
}
it start the new Thread well, but when I am trying to do both:
private class testThread implements Runnable{
#Override
public void run() {
OuterClass.this.myActivity.runOnUiThread(new Runnable(){ ... });
OuterClass.this.myActivity.uiHandler.post(new Runnable(){ ... });
...
nothing happens. UI hang up and no Runnable never run (I see it during careful debugging).
Why? Everything should work or even if it fail, why the UI hangs??
Please help!
SOLVED!!! The problem was in method which invokes getData (outside the scope), it never finished failing into infinite loop. Since that scheduled Runnables never started as I think. Now everything works .
AsyncTask is better. It is easier to use, you don't have to manually manage so many threads, and it keeps your code clean.
The doInBackground() method will do your lengthy task on an alternate thread. If you want to update your UI when the task is running, use the publishProgress() method, and if you want to update the UI after the work is done, use onPostExecute().
As to your question on why the UI hangs, see if you are using any method that takes very long or blocks for some reason in the runOnUiThread() method. If any code takes time, remove it from this method.
Quick question: I have been using frameworks that spawn worker threads to perform asynchronous tasks, a good example is Retrofit. Within the success/failure sections, I may pop up a Dialog box which would need to be on the UI thread. I have been accessing the underlying
Activity/UI thread in this fashion within the success/failure sections of Retrofit:
Dialog dialog = new Dialog(LoginActivity.this, R.style.ThemeDialogCustom);
This works well 99.9% of the time but every once in a while, I receive the following error when creating a Dialog box:
android.view.WindowManager$BadTokenException
LoginActivity.java line 343 in LoginActivity$6.success()
Unable to add window -- token android.os.BinderProxy#41662138 is not valid;
is your activity running?
So, is my approach the most stable way to access the Activity context/UI thread from a worker thread or do I need a different approach?
If you work with threads and not using Asynctasks, always run everything that changes UI in runOnUIThread like this
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
//change UI
}
});
The more generic way to do it is this, which is pretty much the same
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//change UI
}
})
See here the minimal difference between runOnUIThread and MainLooper
If you want to check if you are on the main/ui thread
if(Thread.currentThread() == Looper.getMainLooper().getThread()) {
//you are on the main thread
}
AFAIK, there is nothing wrong with the approach you are using. The problem is occurring because the by the time the worker thread finishes and you are trying to show the dialog, the instance of the Activity has finished. So, the crash is totally dependent on the amount of time it takes for the thread to finish. And it seems that in your case, the thread mostly finishes when the Activity is still active; hence you don't get the error is most cases.
What you need to do is to check if the Activity is still running before trying to show the Dialog. One of the simplest ways would be to
if(!((Activity) LoginActivity.this).isFinishing())
{
//safe to show your dialog
}
From my main thread, I start an AsyncTask which will go through a list of images and for each image, it will do some processing on it. So basically, there's a for loop and inside it, another AsyncTask is called. I use an instance of a class which holds the boolean value for checking if each image is done with its processing, its called a dummyStructure.
Code of the main thread:
new BatchProcessor().execute()
the doInBackground of the BatchProcessor:
protected Void doInBackground(Void... params){
while(dummyStructure.isWorking())
{
//Try loop
thread.sleep(1000);
}
dummyStructure.setIsWorking(true); //basically sets the flag to true
for(String s: pictureList)
{
RunTheProcessingLoop().execute();
}
The Problem:
I tried debugging, and here's what the problem is imo, if I remove the line just outside the while loop dummyStrucutre.setIsWorking(true) then there are multiple asyncTasks called even before it finishes, and basically everything gets screwed up. However, if I don't remove that line, then the BatchProcessor AsyncTask gets caught in the while loop, while as the RunTheProcessingLoop AsyncTask never executes beyond its onPreExecute()(debugged to know that, I used Log.e() in every method of that asyncTask).
Definitely I'm missing something, any help? Thanks a lot! :)
What you are encountering is asynctasks getting piled up because you are starting one from another and not exiting the first. This is because the asynctasks are handled serially by a single thread by default. If you want to use multiple threads in parallel, you'd need to use your own thread executor. See the AsyncTask documentation for more details.
So after 2 days of posting this question and finding out more about what people posted, I figured this:
My main thread called for an AsyncTask and I wanted to wait for that AsyncTask to finish. So I used a boolean flag which the AsyncTask sets to false once it is done and I can queue another task. Here's the code:
class mExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}}
Now, all you need to do is, whatever task/method/etc you want to run Asynchronously, simply create a thread and push it in that class, example:
Thread t = new Thread(new Runnable() {
public void run() {
new someshit().execute();
}
});
new mExecutor().execute(t);
and Tada! Now they both won't be queued/synchronized but would run in parallel.
If I am wrong, please correct me! Thanks! :)
In my Android app, I am extracting the code to update UI elements into a separate utility package for reuse. I would like my code to be proactive and update the UI differently if the current execution context is from a UI thread versus a non-UI thread.
Is it possible to programmatically determine whether the current execution is happening on the UI thread or not?
A trivial example of what I am looking to achieve is this - my app updates a lot of TextViews all the time. So, I would like to have a static utility like this:
public static void setTextOnTextView(TextView tv, CharSequence text){
tv.setText(text);
}
This obviously won't work if called from a non-UI thread. In that case I would like to force the client code to pass in a Handler as well, and post the UI operation to the handler.
Why don't you use the runOnUiThread method in Activity
It takes a runnable and either runs it straight away (if called from the UI thread), or will post it to the event queue of the UI thread.
That way you don't have to worry about if your method has been called from the UI thread or not.
When you're not sure the code is executed on the UI thread, you should do:
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// your code here
}
});
This way, whether you're on the UI thread or not, it will be executed there.
You can use the View post method.
Your code would be:
tv.post(new Runnable() {
public void run() {
tv.setText(text);
}
});
Error
05-12 11:56:45.793: ERROR/AndroidRuntime(505): Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
what i have done
i have a list view inside my activity and i need to populate the listview by doing the following:
mylistview.setAdapter(new CustomAdapter());
now there is already too much pressure on my UI thread so , thought of calling this method inside a AsynTask.
Another problem
there is a progress dialog that shows in my acitivity when the user clicks the button to populate the listview. when i put everything inside one thread the progress dialog does not show. i had asked a question on stackoverflow about why the progress dialog does not show and i had got a reply saying that i need to put all the extra tasks inside another thread.
i have also read the updating UI in android given on android developer website:
but over there all we do is make a new runnable and post the runnable to the Handler of the UI thread so that when the UI is free, the runnable will be executed.
But how does the above solve my purpose? i mean the UI thread is still executing the instructions.
The only way i can take the load of the UI thread is if i make another thread and put all the work over there... but android does not allow this?
what is wrong with my understanding(if there is anything wrong)? How do i solve this problem
thank you in advance.
Android does allow you to put the extra work in a different thread AND publish the results on the UI thread, using AsyncTask. Add the UI update stage in the onPostExecute() method of the AsyncTask and you should be good-to-go. onPostExecute() is performed on the UI thread, the example in the AsyncTask Documentation is a great one.
Also, if you build your application properly, and don't use graphics a lot, there should not be too much work for the UI thread during the application run. Move everything that doesn't absolutely bound to the UI on a separate thread. AsyncTask is a very convenient way to do it.
AsyncTask is the correct way to solve your problem. Where you are running into difficulty is exactly what to put into the AsyncTask. Call mylistview.setAdapter(); from the onProgressUpdate or onPostExecute methods. So do something like this:
void setProgress(Integer progress){ myprogressbar.setValue(progress); }
void setAdapter(CustomAdapter result){ mylistview.setAdapter(result); }
private class LongRunningTask extends AsyncTask<String, Integer, CustomAdapter> {
protected Long doInBackground(String... urls) {
CustomAdapter res = null;
// do all the work to BUILD the custom adapter, calling publishProgress() as progress gets made
publishProgress(<progress value>);
return res;
}
protected void onProgressUpdate(Integer... progress) {
setProgress(progress);
}
protected void onPostExecute(CustomAdapter result) {
setAdapter(result);
}
}
That should fix the threading issue and let you set the progress bar.