Hi I have some questions regarding Looper.prepare() and AsyncTasks.
In my app I have an AsyncTask that's starting other AsyncTasks. I had 2 AsyncTasks Search and GetImage. The GetImage task is executed several times within the Search task. It works fine.
However recently I implemented image caching as described here:
http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
After I implemented this I started getting intermittent crashes
02-09 17:40:43.334: W/System.err(25652): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I'm not sure where I should call prepare(). Here's a rough outline of the code
Search extends AsyncTask{
#Override
protected void doInBackground(){
ArrayList<Object> objs = getDataFromServer();
ArrayList<View> views = new ArrayList<View>();
for(Object o: objs){
//multiple AsyncTasks may be started while creating views
views.add(createView(o));
}
for(View v: views){
publishProgess(v);
}
}
}
public View createView(Object o){
//create a view
ImageView iv = .....;
ImageDownloader.getInstance().download(url,iv);
}
The ImageDownloader can be seen in the link above, in it is another AsyncTask to download the image aswell. It also contains a Handler and Runnable for purging the cache which is reset every time a download occurs. I did make one change to the ImageDownloader, I made it a singleton.
public static ImageDownloader getInstance(){
if(instance == null){
//tried adding it here but it results in occasional
//cannot create more than one looper per thread error
Looper.prepare();
instance= new ImageDownloader();
}
return instance;
}
The ImageDownloader download method may be called 10's of times, which is creating an AysncTask for each of the downloads. So I've been scratching my head for the last few days, Hope you guys can help.
What is really going on is you are attempting to perform something on a background thread that requires the UI thread to run.
The Looper is a part of the system that ensures that transactions are done in order, and the device is responding as it should.
95% of the time when you get the Looper error, what it really means is you need to move part of your code to the UI thread, in Asynctask this means moving it to either onPostExecute or onProgressUpdate.
In your case it appears as if you are adding views, which is part of the UI and therefor would cause a problem. If that is not in fact what is causing the problem, an examination of the stacktrace should give you some clues.
As a side note, if you MUST call Looper.prepare() I would call it at the beginning of your thread. However, this is generally recommended to avoid the need to call it.
Related
AsyncTask is a standard way to perform long running operations asynchronously on a background thread without holding up the UI thread. One should not perform any UI interactions from the doInBackground() method.
My question: What are examples of UI interactions that are forbidden? Would it be any of the following:
LayoutInflater.inflate()
View.findViewById()
TextView.setText()
I'm inclined to say yes, but we have some code right now that does all of these (and more) and is called from the doInBackground() method, and yet the code is working. I've seen other people indicate they receive an exception when attempting to perform UI activity from doInBackground(), but that is not our experience.
Our code is generating an on-screen report that is not visible until the entire operation is complete. On rare occasion (hard to reproduce) when attempting to cancel the operation very quickly, we will see the application get into a weird state, but it doesn't crash.
Before changing our code in hopes of finding this rare condition, I wanted to see if anyone had some thoughts on why our code is "working" as-is.
The only other tidbit of information that might be helpful is that our doInBackground method has the following code template:
protected Boolean doInBackground(Void... voids) {
if (null == Looper.myLooper()) {
Looper.prepare();
}
publishProgress(0.0);
// Perform ui/non-ui logic here
Looper myLooper = Looper.myLooper();
if (null != myLooper && Looper.getMainLooper() != myLooper) {
myLooper.quit();
}
return true;
}
The Looper is needed for some of the report generating code (omitted) that uses a new Handler() to generate data. I'm not sure if creating the Looper is somehow making our ui interactions legal.
(I do have a stack trace that clearly shows our UI activity being called from doInBackground, in case you thought we might be spinning off some separate threads to update our UI)
AsyncTask is not meant for really long running work, it should complete within a few seconds. It is a one-shot completely managed thread context, which should not have its own Looper attached to it. That actually will break the backing AsyncTask functionality - starving off other future AsyncTask operations you may be starting. If you have something which requires a Looper, you should be using your own Thread or ThreadPool rather than an AsyncTask. You'll also want to make sure you retain a reference to your AsyncTask so it can be cancelled appropriately - this is a source of many memory leaks and/or exceptions due to invalid state when onPostExecute() is called.
The intent of the publishProgress() method is to give your app the ability to get updates it can reflect on the UX. You are correct, setText(), etc. should not be run in the doInBackground() callback. That callback is executed in arbitrary thread context in which you do not control and cannot make UI updates.
You may be able to use inflateLayout() and findViewById(), but this is not a good practice to do this outside of initialization as these are potentially expensive operations. Inflation has to parse the binary layout and create view objects on the fly. Finding by ID walks the entire view hierarchy to find the component you desire. A better practice would be to cache these at creation (for an Activity or Fragment) or when creating a view as part of an adapter (such as a ViewHolder in RecyclerView.
I would expect this (inside getView()) to work...
but nothing happens, the bitmaps are not loaded into the ImageView....
Thread th= new Thread(new Runnable() {
#Override
public void run() {
ImageLoader.getInstance().displayImage(rss.getChannel().getItems().get(position).getEnclosure().getUrl(),holder.imageView,options,new ImageLoadingListener() {
#Override
public void onLoadingStarted(String imageUri, View view) {
//...stuff...
}
//... more stuff
#Override
public void onLoadingCancelled(String imageUri, View view) {
//...stuff
}
});
}
});
th.start();
Please why it's not???
Thanks :-)
What you try to do is really bad practice. However, I cant tell you from the information you provide us what the problem is.
I think that the ImageLoading library is already doing things in its own thread, therefore your Thread is already finished. Also only the main UI Thread can access or manipulate UI Views, but there should be an exception thrown if this would be the problem.
Furthermore, I would recommend you to use Picasso. It handles Threadpooling, scheduling, caching, memory management (avoid memory leaks by using WeakReference) and recycling view in ListView or RecyclerView.
Update:
You are creating a Thread everytime getView() is called while scrolling. This is bad practice, because creating a Thread is expensive. Think about flinging or fast scrolling a list. If you scroll 20 elements, 20 Threads will be created and started. All the thready have the same priority as the main UI thread. That means that the CPU is shared between 20 Threads plus the main UI Thread. This could lead to bad UI performance. You should use a ThreadPool. Furthermore, you don't stop your Thread. This brings two problems:
Your view get recycled in getView() while the Thread is not, so the thread is still running while the view should display already another item.
You get memory leaks! Do you know how Garbage Collection works? It starts at thread level and searches for unused objects (objects that are not referenced anymore by any other object). Your Thread is a inner class. Therefore it has a hard reference to the surrounding Adapter class. The Adapter is attached to the ListView, the ListView to the Activity. So if your Activity gets closed by the user the garbage collector can not collect Activity and all the views and other objects until the Thread (or in your case, all Threads) are finished. You probably run out of memory.
Last but not least, the Android System has its own message queue based threading system and only the main UI Thread can access UI Views. I don't want to get to deep in detail here, I'm sure you can google it. At this point I recommend to use AsyncTask if you really have to start your own Thread. But you also have to cancel the AsyncTask manually to avoid memory leaks.
First of all, Android application UI not updated or accessed from worker thread. So your Thread not going to change/access Image.
Second, you don't need Thread for Image loading, as your ImageLoader library itself take care of it.
Only the UIThread can change its views. I suggest to use runOnUIThread() method from Activity.
More info: https://developer.android.com/training/multiple-threads/communicate-ui.html
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! :)
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);
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.