[android-developers] Re: Lazy-load images into a ListView
Evan Charlton wrote: > I will be surprised if this hasn't been asked before, but I've been > Googling for days now and haven't found anything very helpful. My > problem is this: > > I'm trying to lazy-load images into a ListView--almost exactly how > Market does it, if you're familiar with that. It's not exactly like > Market because the images there comes in batches whereas mine are one > at a time, but I digress... > > I have the following adapter: http://paste2.org/p/318634 > This adapter lazy-loads pages and each row's corresponding image (if > applicable) and caches it to disk for faster loading next time. This > all works perfectly *except* when loading images over the network, > they come in on the wrong rows and are sometimes duplicated. I assume > that this has to do with the adapter reusing views, but I haven't been > able to stamp it out. I suspect you are on the money with your assessment of the problem. You pass in an ImageView to the AsyncTask and tell the task to replace the image in that ImageView. However: -- Since it is asynchronous, for a bit, the ImageView will remain unchanged, which in the case of a recycled row will be whatever it was before -- If you scroll enough before the image is done loading, and the row is recycled first, you'll get weird results (image will keep flipping to different pictures as its various tasks complete) I just went through implementing something very similar. I'll eventually tease it out into something reusable and open source. Compared to the snippet of your implementation, here are two things I did differently: 1. Rather than tie the AsyncTask to a specific ImageView, I have the AsyncTask notify the ListView when an image load is complete, providing it the URL of the loaded image. The ListView, when it inflates/recycles rows, attaches the URL of the image that is supposed to be shown to each ImageView via the setTag() method. Then, when the AsyncTask says such-and-so URL is ready, the ListView walks the ImageViews and sees if any are showing that image, by comparing the passed-in URL to each ImageView's getTag(). If there's a match, ListView then loads the image out of the AsyncTask-managed cache into the ImageView. This way, I don't keep a strict tie between the AsyncTasks and the ImageViews. I suspect there's a better-performing solution than my current implementation, but it's at least tolerable for the moment. 2. Rather than try to hack around the existing AsyncTask's limitations, I copied its source code (thank you, Android and Apache 2!) and made it use an unbounded LinkedBlockingQueue and use more than one thread to process it. That beats the heck out of trying to keep your own queue to feed into the AsyncTask class's static queue. -- Mark Murphy (a Commons Guy) http://commonsware.com | http://twitter.com/commonsguy Android 1.5 Programming Books: http://commonsware.com/books.html --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers-unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en -~--~~~~--~~--~--~---
[android-developers] Re: Lazy-load images into a ListView
Mark, Thanks for the reply! > -- If you scroll enough before the image is done loading, and the row is > recycled first, you'll get weird results (image will keep flipping to > different pictures as its various tasks complete) Even if I never scroll, the results are still jumbled and/or duplicated. > 1. Rather than tie the AsyncTask to a specific ImageView, I have the > AsyncTask notify the ListView when an image load is complete, providing > it the URL of the loaded image. The ListView, when it inflates/recycles > rows, attaches the URL of the image that is supposed to be shown to each > ImageView via the setTag() method. Then, when the AsyncTask says > such-and-so URL is ready, the ListView walks the ImageViews and sees if > any are showing that image, by comparing the passed-in URL to each > ImageView's getTag(). If there's a match, ListView then loads the image > out of the AsyncTask-managed cache into the ImageView. This way, I don't > keep a strict tie between the AsyncTasks and the ImageViews. I suspect > there's a better-performing solution than my current implementation, but > it's at least tolerable for the moment. I'll code this up later and see if I have any problems. > 2. Rather than try to hack around the existing AsyncTask's limitations, > I copied its source code (thank you, Android and Apache 2!) and made it > use an unbounded LinkedBlockingQueue and use more than one thread to > process it. That beats the heck out of trying to keep your own queue to > feed into the AsyncTask class's static queue. Haha, yeah, that code could be cleaner, but it works pretty reliably, so I'm going to avoid replacing it until it becomes the problem. Evan Charlton On Sun, Jul 12, 2009 at 6:29 AM, Mark Murphy wrote: > > Evan Charlton wrote: >> I will be surprised if this hasn't been asked before, but I've been >> Googling for days now and haven't found anything very helpful. My >> problem is this: >> >> I'm trying to lazy-load images into a ListView--almost exactly how >> Market does it, if you're familiar with that. It's not exactly like >> Market because the images there comes in batches whereas mine are one >> at a time, but I digress... >> >> I have the following adapter: http://paste2.org/p/318634 >> This adapter lazy-loads pages and each row's corresponding image (if >> applicable) and caches it to disk for faster loading next time. This >> all works perfectly *except* when loading images over the network, >> they come in on the wrong rows and are sometimes duplicated. I assume >> that this has to do with the adapter reusing views, but I haven't been >> able to stamp it out. > > I suspect you are on the money with your assessment of the problem. > > You pass in an ImageView to the AsyncTask and tell the task to replace > the image in that ImageView. However: > > -- Since it is asynchronous, for a bit, the ImageView will remain > unchanged, which in the case of a recycled row will be whatever it was > before > > -- If you scroll enough before the image is done loading, and the row is > recycled first, you'll get weird results (image will keep flipping to > different pictures as its various tasks complete) > > I just went through implementing something very similar. I'll eventually > tease it out into something reusable and open source. Compared to the > snippet of your implementation, here are two things I did differently: > > 1. Rather than tie the AsyncTask to a specific ImageView, I have the > AsyncTask notify the ListView when an image load is complete, providing > it the URL of the loaded image. The ListView, when it inflates/recycles > rows, attaches the URL of the image that is supposed to be shown to each > ImageView via the setTag() method. Then, when the AsyncTask says > such-and-so URL is ready, the ListView walks the ImageViews and sees if > any are showing that image, by comparing the passed-in URL to each > ImageView's getTag(). If there's a match, ListView then loads the image > out of the AsyncTask-managed cache into the ImageView. This way, I don't > keep a strict tie between the AsyncTasks and the ImageViews. I suspect > there's a better-performing solution than my current implementation, but > it's at least tolerable for the moment. > > 2. Rather than try to hack around the existing AsyncTask's limitations, > I copied its source code (thank you, Android and Apache 2!) and made it > use an unbounded LinkedBlockingQueue and use more than one thread to > process it. That beats the heck out of trying to keep your own queue to > feed into the AsyncTask class's static queue. > > -- > Mark Murphy (a Commons Guy) > http://commonsware.com | http://twitter.com/commonsguy > > Android 1.5 Programming Books: http://commonsware.com/books.html > > > > --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to android-developers@googlegroups.com To unsubscrib
[android-developers] Re: Lazy-load images into a ListView
What if you have the listview load the images from a hashmap, and have a separate process fill the hashmap in the background? On Jul 12, 3:47 pm, Evan Charlton wrote: > Mark, > > Thanks for the reply! > > > -- If you scroll enough before the image is done loading, and the row is > > recycled first, you'll get weird results (image will keep flipping to > > different pictures as its various tasks complete) > > Even if I never scroll, the results are still jumbled and/or duplicated. > > > 1. Rather than tie the AsyncTask to a specific ImageView, I have the > > AsyncTask notify the ListView when an image load is complete, providing > > it the URL of the loaded image. The ListView, when it inflates/recycles > > rows, attaches the URL of the image that is supposed to be shown to each > > ImageView via the setTag() method. Then, when the AsyncTask says > > such-and-so URL is ready, the ListView walks the ImageViews and sees if > > any are showing that image, by comparing the passed-in URL to each > > ImageView's getTag(). If there's a match, ListView then loads the image > > out of the AsyncTask-managed cache into the ImageView. This way, I don't > > keep a strict tie between the AsyncTasks and the ImageViews. I suspect > > there's a better-performing solution than my current implementation, but > > it's at least tolerable for the moment. > > I'll code this up later and see if I have any problems. > > > 2. Rather than try to hack around the existing AsyncTask's limitations, > > I copied its source code (thank you, Android and Apache 2!) and made it > > use an unbounded LinkedBlockingQueue and use more than one thread to > > process it. That beats the heck out of trying to keep your own queue to > > feed into the AsyncTask class's static queue. > > Haha, yeah, that code could be cleaner, but it works pretty reliably, > so I'm going to avoid replacing it until it becomes the problem. > > Evan Charlton > > On Sun, Jul 12, 2009 at 6:29 AM, Mark Murphy wrote: > > > Evan Charlton wrote: > >> I will be surprised if this hasn't been asked before, but I've been > >> Googling for days now and haven't found anything very helpful. My > >> problem is this: > > >> I'm trying to lazy-load images into a ListView--almost exactly how > >> Market does it, if you're familiar with that. It's not exactly like > >> Market because the images there comes in batches whereas mine are one > >> at a time, but I digress... > > >> I have the following adapter:http://paste2.org/p/318634 > >> This adapter lazy-loads pages and each row's corresponding image (if > >> applicable) and caches it to disk for faster loading next time. This > >> all works perfectly *except* when loading images over the network, > >> they come in on the wrong rows and are sometimes duplicated. I assume > >> that this has to do with the adapter reusing views, but I haven't been > >> able to stamp it out. > > > I suspect you are on the money with your assessment of the problem. > > > You pass in an ImageView to the AsyncTask and tell the task to replace > > the image in that ImageView. However: > > > -- Since it is asynchronous, for a bit, the ImageView will remain > > unchanged, which in the case of a recycled row will be whatever it was > > before > > > -- If you scroll enough before the image is done loading, and the row is > > recycled first, you'll get weird results (image will keep flipping to > > different pictures as its various tasks complete) > > > I just went through implementing something very similar. I'll eventually > > tease it out into something reusable and open source. Compared to the > > snippet of your implementation, here are two things I did differently: > > > 1. Rather than tie the AsyncTask to a specific ImageView, I have the > > AsyncTask notify the ListView when an image load is complete, providing > > it the URL of the loaded image. The ListView, when it inflates/recycles > > rows, attaches the URL of the image that is supposed to be shown to each > > ImageView via the setTag() method. Then, when the AsyncTask says > > such-and-so URL is ready, the ListView walks the ImageViews and sees if > > any are showing that image, by comparing the passed-in URL to each > > ImageView's getTag(). If there's a match, ListView then loads the image > > out of the AsyncTask-managed cache into the ImageView. This way, I don't > > keep a strict tie between the AsyncTasks and the ImageViews. I suspect > > there's a better-performing solution than my current implementation, but > > it's at least tolerable for the moment. > > > 2. Rather than try to hack around the existing AsyncTask's limitations, > > I copied its source code (thank you, Android and Apache 2!) and made it > > use an unbounded LinkedBlockingQueue and use more than one thread to > > process it. That beats the heck out of trying to keep your own queue to > > feed into the AsyncTask class's static queue. > > > -- > > Mark Murphy (a Commons Guy) > >http://commonsware.com|http://twitter.com/commonsgu
[android-developers] Re: Lazy-load images into a ListView
Christine wrote: > What if you have the listview load the images from a hashmap, and have > a separate process fill the hashmap in the background? That's effectively what I'm doing, but you still need to tell the ListView when images are ready to be loaded out of the HashMap. That's for the case where the ListView is trying to display one of the images but does not have it yet, substituting some placeholder instead until the image is downloaded. -- Mark Murphy (a Commons Guy) http://commonsware.com | http://twitter.com/commonsguy Need Android talent? Ask on HADO! http://wiki.andmob.org/hado --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers-unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en -~--~~~~--~~--~--~---
[android-developers] Re: Lazy-load images into a ListView
My app loads new data like every four minutes, and if the pic is not there in time, too bad, you'll have it in the next round. What you can do is if the hashmap returns null, subsitute a default pic, which I gues you have to do because you'll probably have a recycled view which already contains a pic - but the wrong one. It gets really weird when you have different views for the items in the listview, like when the user changes the layout, or you have different layouts in portrait mode and landscape, or all of the above. I chose to trade in options for better recycling of views. Another issue is, do you want to spend a lot of time at startup preparing for the listview so the scrolling is smooth, but the user has to wait a little longer, or do you want to display views quickly, at the cost of bumpy scrolling? At any rate, the simpler the UI, the better. On Jul 12, 5:57 pm, Mark Murphy wrote: > Christine wrote: > > What if you have the listview load the images from a hashmap, and have > > a separate process fill the hashmap in the background? > > That's effectively what I'm doing, but you still need to tell the > ListView when images are ready to be loaded out of the HashMap. That's > for the case where the ListView is trying to display one of the images > but does not have it yet, substituting some placeholder instead until > the image is downloaded. > > -- > Mark Murphy (a Commons > Guy)http://commonsware.com|http://twitter.com/commonsguy > > Need Android talent? Ask on HADO!http://wiki.andmob.org/hado --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers-unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en -~--~~~~--~~--~--~---