Android开发之异步回调中的UI同步性的解决方案
白羽 2018-06-07 来源 :网络 阅读 1153 评论 0

摘要:凡此类“Android异步回调UI同步性问题”,最好都通过比较“异步回调生成点”和“异步回调执行点”特征变量的值去针对性的做逻辑处理,以免出现不必要的Bug,是非常必要且有效的手段。




Android程序编码过程中,回调无处不在。从最常见的Activity生命周期回调开始,到BroadcastReceiver、Service以及Sqlite等。Activity、BroadcastReceiver和Service这些基本组件的回调路径和过程也就是通常意义上所谓的“生命周期”。同时,在处理具体的业务逻辑时,常常设计到不同线程之间的通信,如下载图片完成后通知 UI线程更新UI,凡此类场景,无论使用哪一种具体的线程间通信方式(Handler/Message、Handler/post、基于接口的回调、基于多对多的观察者模式如EventBus等),其本质上都是基于“回调”。在实际编码过程中,凡涉及到不同线程之间的通信,本质上更是属于“异步回调”。当需要在“异步回调”中修改UI时,此时需要特别注意UI同步性问题。


为了便于问题的阐述,在此先对“Android异步回调UI同步性问题”进行如下界定:当异步回调执行时(称之为“异步回调执行点”),当前UI界面上的元素与最初生成此异步回调的调用器开始执行时(称之为“异步回调生成点”)的UI元素已经存在不一致,不一致不仅包括UI元素可能的界面变化、可能的内容变化,也包括“异步回调执行点”和“异步回调生成点”时的UI元素中的某一特性的表征量(如某一具有表征当前UI元素的字段值)相关,即使UI元素界面和内容都尚未发生变化。


编码过程中,“Android异步回调UI同步性问题”经常存在,有时候稍不注意会产生一些看起来难以理解的bug,并由于异步特性的存在,此类bug还具有一定的随机性。有时候由于一些需求的复杂性,此类bug隐蔽性很强,也容易被忽略。至少到目前为止,在实际开发中,本人遇到此类问题已有数个。


纯文字的描述可能不太好理解,下面以一个很常用的Android-Universal-Image-Loader为例,简单举例一个潜在存在的“Android异步回调UI同步性问题”。


ListView Item View中有ImageView,通过Android-Universal-Image-Loader去加载显示,图片加载完成后需要做一些逻辑处理(如隐藏图片加载进度条等..),通常代码如下:

 

 1 ImageLoader.getInstance().loadImage(imageUrl, new ImageLoadingListener() { 2                                  3     @Override 4     public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { 5         if (loadedImage != null) { 6             imageView.setImageBitmap(loadedImage); 7             // 其他业务逻辑处理.. 8         } 9     }10 11     @Override12     public void onLoadingStarted(String imageUri, View view) {13         14     }15 16     @Override17     public void onLoadingCancelled(String arg0, View arg1) {18         19     }20 21     @Override22     public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {23         24     }25 });

 

初看上去,代码逻辑好像也没什么问题,大部分人也是这么写的。当较慢滑动ListView时,或在平时正常使用时,也没有什么问题。但是此处的代码逻辑真的严密吗?

ListView的getView复用特性,大家也都熟知。对于之前遇到的“图片错位/先显示之前的图片后再被正确的图片覆盖掉”,此类现象也都知道如何解决(在getView逻辑开始处理处将ImageView设置成最先的默认图片,其他UI元素类似处理),基本上也不会再有“图片错位/先显示之前的图片后再被正确的图片覆盖掉”这类现象了。实际上,当网速条件一般,且loadImage大致与上述代码所示,在ListView中快速滑动列表,几屏后,不出意外,会发现“图片错位/先显示之前的图片后再被正确的图片覆盖掉”此问题依然存在。


此时问题出现的原因不在于getView本身,因为getView逻辑开始时已经将ImageView重置为默认图片,而在于“Android异步回调UI同步性问题”。由于ViewHolder的不断复用,网速一般时快速滑动几屏后,onLoadingComplete的异步回调执行时与当前UI元素已经存在不一致,简单点理解,ImageView被复用了ImageView position 0,ImageView position 11, ImageView position 21,此时滑动停止,onLoadingComplete的异步回调执行时ImageView已经是最后一次的ImageView position 21,而onLoadingComplete的异步回调可能被执行数次(ImageView position 0,ImageView position 11, ImageView position 21,且顺序还取决于异步中的具体处理和网络环境等),于是问题发生了。


解决方案:

抓住”UI元素中的某一特性的表征量“,在异步回调中通过比较“异步回调生成点”和“异步回调执行点”此特征变量的值直接作出逻辑上的处理。

 

 1 public class HardRefSimpleImageLoadingListener implements ImageLoadingListener { 2  3     public int identifier; 4  5     public HardRefSimpleImageLoadingListener() { 6     } 7  8     public HardRefSimpleImageLoadingListener(int identifier) { 9         this.identifier = identifier;10     }11 12     @Override13     public void onLoadingCancelled(String arg0, View arg1) {14 15     }16 17     @Override18     public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {19 20     }21 22     @Override23     public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {24 25     }26 27     @Override28     public void onLoadingStarted(String arg0, View view) {29     30     }31 }32 33 ImageLoader.getInstance().loadImage(imageUrl, new HardRefSimpleImageLoadingListener(did) {34     @Override35     public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {36         if (loadedImage != null) {37             if (identifier != did) {38                 return;39             }40             imageView.setImageBitmap(loadedImage);41             // 其他业务逻辑处理..42         }43     }44 });

 

总之,凡此类“Android异步回调UI同步性问题”,最好都通过比较“异步回调生成点”和“异步回调执行点”特征变量的值去针对性的做逻辑处理,以免出现不必要的Bug,是非常必要且有效的手段。

 

 


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程