Android: why Thread in getView() does not seem to work? - android

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

Related

What constitutes "UI interactions" for AsyncTask?

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.

Is it okay to read data from UI elements in another thread?

I just found out that some of my code is (unindentionally) running in a worker thread and reads some data from UI elements:
e.g. checkbox.isChecked(), textView.getText()
and to my surprise, this works just fine...
I expected that it would crash with an exception (like the following exception that I get when I want to write to UI elements):
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
According to the Android docs:
Only objects running on the UI thread have access to other objects on
that thread. Because tasks that you run on a thread from a thread pool
aren't running on your UI thread, they don't have access to UI
objects.
So, is it really okay to read data from UI elements from other threads?
If not: why is there no exception?
is it really okay to read data from UI elements from other threads?
No, but not for the reasons that you may be thinking.
First, as soon as you fork the background thread, the user might press BACK or otherwise do something that destroys your activity. It is not safe to try to use widgets of a destroyed activity.
Second, there is little to no thread synchronization logic in the View class or its subclasses. The main application thread may be modifying the data at the same time that you are trying to use it, resulting in possible concurrency issues.
I would pass the data from the widgets into the thread (e.g., thread constructor).
why is there no exception?
AFAIK, that specific exception is only thrown on setters or other things that modify the contents of the widget. This does not mean that any code that does not raise that exception is guaranteed to be safe.
You can't redraw (invalidate) your Views outside main thread which is also UI thread. Setting text for TextView also causes redrawing view ofc... But getting text just returns String (or Editable?), so its working...
Note that there are some other restrictions and limitations, e.g. when you pass EditText to some AsyncTask, but while it's working the main Activity (holding passed view) finish then you might get NPE, because view is also gone... In this and similar cases WeakReferences are very usefull

Why Should We Use AsyncTask with a Return , Instead Running in UI Thread?

i have Trouble Understanding 1 Thing in Populating ListView, as My Experience says Methods Which run on UI Thread have most priority and so run alot faster than methods run in backGround but Difference is The UI Thread Will not Wait for Them to Finish so We Dont Feel any Lag in it.
But When We Using AsyncTask With a Result, Like This:
Class async extends AsyncTasl<Void,Void,Bitmap>{
#Override
public Bitmap doInBackGround(Void...Params){
.
.
.
return new bitMap;
}
}
since it returns some thing, so the MainThread MUST wait for it to finish, Which negates main purpose of running in backGround, So am i understanding Some thing Wrong, Or There is really no reason to do Such things in BackGround while they can run even Faster on UI Thread?
UPDATE :
my point is when we Use async.execute().get()
The UI thread behaves like any other thread that executes code, but as you pointed out, the main difference is that it's the thread that the user interacts with.
Picture this scenario:
You need to load list items that have an icon attached to them, and that icon lives at a URL and must be retrieved with a network request.
Here's some example code that fetches the image located at a URL whenever a list item appears on the screen:
public View getView(int position) {
final URL imageURL = getURL(position);
final Bitmap image = getImage(imageURL); //this will take a while
...
view.setIcon(imageIcon);
return view;
}
When working with list views on Android, that kind of call to getView happens whenever the user swipes through the list, as each list item appears on the screen.
If that's all done on the UI thread, the user is going to see a list that takes an agonizingly long time to load each list item. The main thread is not smart enough to cut long-running tasks short (what if that long running task happened to be saving important user data to an external DB somewhere?), so it has to let them finish entirely. It's also important to note that the UI Thread is not much different from any other thread from a system perspective. It's important because it's where the user experience happens.
That's why a best practice when writing any application on any platform is to offload long-running tasks to a background thread. The OS can handle switching between threads, which results in a UI that is extremely responsive to user actions (like swiping through a list).
As ABFORCE demonstrated, you should fetch images with an AsyncTask on Android. In the background, a request will be made to the URL's for the image, and only when the final image is ready to be shown on the page will a call be made to the UI thread to display it. The user would see a list that they can swipe through quickly, and images would pop into each list item as they are fetched. We don't want to hold the user up from doing something, so we want to make sure they can swipe through the list as fast as they want.
UPDATE:
If you were to do new AsyncTask().execute.get(), that method would indeed block on the UI thread, negating the benefits of a background thread. What you may need to do in your class that extends AsyncTask is hold a reference to the View you want your image a part of (I'm assuming a new AsyncTask per list item), and in onPostExecute, set whatever field that needs to be set on that view.
Example:
class async extends AsyncTask<Void,Void,Bitmap>{
private View view;
#Override
public Bitmap doInBackGround(Void...Params) {
/* Here is background thread */
return new Bitmap(); ----+
} |
| //this object will be passed here
#Override V
public void onPostExecute(Bitmap result) {
/* Here is main thread */
view.setIcon(result);
}
}
elsewhere...
public View getView(int position) {
new async(view).execute();
return view; //View will return now, and the image will populate later
}
There are obviously many ways to do this, and that's one off the top of my head.
AsyncTasks are a good way to perform operations in the background without blocking the main UI thread.
When you call asyncTask.execute(),
onPreExecute(..) callback is issued on the UI thread
doInBackground(..) is called on a background thread (If I understand your question correctly, the main thread is not waiting here). When you get the data from a server (say) in this method, you return the data so that the Android system will call onPostExecute with this data on the UI thread
onPostExecute(..) is issued on the UI thread with the data you returned in doInBackground(..) as a parameter.
No, main thread does not wait for the task to be complete, That return value will be passed to onPostExecute(...) method. This method will be run on main thread and takes that return value as its own input argument.
Class async extends AsyncTasl<Void,Void,Bitmap>{
#Override
public Bitmap doInBackGround(Void...Params){
/* Here is background thread */
return new Bitmap(); ----+
} |
| //this object will be passed here
#Override V
public void onPostExecute(Bitmap result){
/* Here is main thread */
}
}

Altering UI thread's Views in AsyncTask in doInBackground, CalledFromWrongThreadException not always thrown

The following code in an AsycnTask:
#Override
protected Boolean doInBackground(View... params) {
try{
Drawable drawPhoto = DataDatero.ImageDownload(taskPhotoName);
((ImageView)params[0]).setImageDrawable(drawPhoto);
((TextView)params[1]).setText(taskItemListText);
((TextView)params[2]).setTextColor(taskColore);
((TextView)params[2]).setText(taskItemStockText);
[...]
}
Throws a CalledFromWrongThreadException , describing that:
Only the original thread that created a view hierarchy can touch its
views
This has been discussed in plenty of questions: example , another example ; and all reach the same conclusion.
But what I'm not only getting the Exception..
I call that execute from a getView in a List adapter, and the images (params[0]) are updated in the view while the exception is thrown for the text. Also, if I leave only the textview updates , the text is updated and no exception is thrown.
If I update the TextView first and the ImageView after, some text and images are updated while some images are not (that imageDownload includes a 2-3 sec network operation)
Why some of them are updated and others are not?
Notes: this was tested only with sdk 4.0, v4 support, on api 16 emulation.
I fixed it and I'm not touching the views in doInBackground
The second example is something similar... is it that the operation gets validated if onCreate is not finished?
I have come across a similar issue and asked a question here (self answered after a good bit of digging).
Essentially what it boils down to is that, contrary to what everybody thinks, you can modify UI elements from an AsyncTask execute() if those views haven't gone through a layout traversal yet. This happens asynchronously to the main flow of execution (activity lifecycle methods/callbacks), so if the View in question is created shortly before execute() is called, you can access them (meaning, the exception isn't thrown, it's of course still really bad practice and not advisable). Because execute() happens on another thread, the layout traversals (which run on the UI thread) may finish while your execute() is running, which explains why only some views may be modified and others throw the exception. It also explains why "leaving only the textview updates" (and presumably removing the ImageView updates) results in those updates "magically" working too. Since this is a timing related issue, it depends on many factors, among other things how long Drawable drawPhoto = DataDatero.ImageDownload(taskPhotoName); takes to run.
PS: I realise this is a late answer, but I think this can be useful for somebody finding this answer first, there aren't many posts dealing with issues like this.
The exception is clear enough. You can not update UI element from a thread different from the UI Thread. doInBackground executes code in a different thread
Why cant you pass the information to update the UI to the onPostExecute method? This is where the UI is intended to be updated.
When you run the execute method of your task, the doInBackground method is executed in a background thread.
And you are not allowed to modify UI from a background thread.
So, don't modify the UI in the doInBackground method.
You should do this UI stuff in onPostExecute method instead, which is guaranteed to be executed in UI thread.

Android Looper.prepare() and AsyncTask

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.

Categories

Resources