android - Posting a runnable to a View that invalidates the View sometimes doesn't work -
i been fighting odd issue these last few days. have custom expandablelistadapter each row contains imageview, among other things. have class handles asynchronous loading of images multitude of places may reside (disk cache, app data, remote server, etc). in adapter's getview method delegate responsibility of returning view list item (i have multiple row types group list). request image load follows:
final imageview thumb = holder.thumb; holder.token = mfetcher.fetchthumb(mimage.id, new bitmapfetcher.callback() { @override public void onbitmap(final bitmap b) { thumb.post(new runnable() { @override public void run() { thumb.setimagebitmap(b); } }); } @override public void onfailure() { } });
yeah, it's ugly, decided against contract have bitmapfetcher.callback execute methods on ui thread default.
anyway, when load activity contains expandablelistview there thumb images missing different rows in list. reloading activity may cause of missing thumbs show others showing may not anymore. behavior pretty random far can tell. scrolling listview such rows missing images recycled causes new thumb images (when recycled row gets displayed again) load fine. scrolling rows contained missing images causes missing images appear. can confirm images loading correctly bitmapfetcher (mfetcher) class. should mention load other images in other places. every once in awhile don't appear either.
after pulling of hair out, discovered changing:
thumb.post(new runnable() {
to:
mexplistview.post(new runnable() {
fixes issue. thought issue might happening because using final reference view, other locations in app use non-final references view post messages, and, mentioned, did not work. changed use activity's runonuithread() method (and own getuithreadrunner().execute method when inside fragments) , seems fix issue around.
so question remains, in cases can view.post() fail deliver runnable associated viewroot's message queue in proper order? or, perhaps invalidate() happening before view returned getview , before it's placed in viewgroup can reached root view. cases can think of prevent image showing up. can guarantee none of these calls happening until @ least onstart has finished executing. further, looks it's fine post view if hasn't been attached window yet:
// execute enqueued actions on every traversal in case detached view enqueued action getrunqueue().executeactions(attachinfo.mhandler);
(in performtraversal). difference between runonuithread , post seems activity has different handler viewrootimpl.
activity:
final handler mhandler = new handler();
whereas in viewrootimpl:
final viewroothandler handler = new viewroothandler();
but, should not problem provided both handlers constructed in same thread (or using same looper). leaves me wondering if is, indeed, problem invalidate() view has not yet been added hierarchy. case invalidate should either 1. not if it's not visible, or 2. valid next performtraversal() happens.
view.invalidate() checks nice private method that's not documented called skipinvalidate():
/** * not invalidate views not visible , not running animation. * not drawn , should not set dirty flags if drawn */ private boolean skipinvalidate() { return (mviewflags & visibility_mask) != visible && mcurrentanimation == null && (!(mparent instanceof viewgroup) || !((viewgroup) mparent).isviewtransitioning(this)); }
it looks number 1 more accurate! however, think pertains view's visibility property. so, accurate assume view considered not visible if cannot reached viewroot? or visibility property unaffected view's container? if former case (which suspect is) raises concern. use of activity.runonuithread not solution problem. happens work because invalidate() calls being sent different handler , being executed later (after getview returns , after row has been added , made visible on screen). has else run issue? there solution?
hey david ran similar issue long time back. basic requirement view.post(runnable r)
view
should attached window runnable executed. however, since loading images asynchronously in first case, therefore there probability imageview
aren't attached window when post request made , hence, images fail load.
quoting earlier version of docs on same:
view.post() : causes runnable added message queue. runnable run on user interface thread. method can invoked outside of ui thread only when view attached window.
switching next question, best solution handle situation ?
can't comment on best solution. however, think both handler.post()
, activity.runonuithread()
go. since, post runnable in main thread queue irrespective of , in general, request display list rows enqueued prior our thumb.post()
. so, might work flawlessly cases. (atleast i've never faced problem them !). however. if find better solution, share me.
Comments
Post a Comment