当前位置: 移动技术网 > IT编程>移动开发>Android > Android异步回调中的UI同步性问题分析

Android异步回调中的UI同步性问题分析

2019年07月24日  | 移动技术网IT编程  | 我要评论

民谣吉他谱网,抹平机,恋爱是从告白开始的全集

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去加载显示,图片加载完成后需要做一些逻辑处理(如隐藏图片加载进度条等..),通常代码如下:

imageloader.getinstance().loadimage(imageurl, new imageloadinglistener() {
        
 @override
 public void onloadingcomplete(string imageuri, view view, bitmap loadedimage) {
  if (loadedimage != null) {
   imageview.setimagebitmap(loadedimage);
   // 其他业务逻辑处理..
  }
 }

 @override
 public void onloadingstarted(string imageuri, view view) {
  
 }

 @override
 public void onloadingcancelled(string arg0, view arg1) {
  
 }

 @override
 public void onloadingfailed(string arg0, view arg1, failreason arg2) {
  
 }
});

初看上去,代码逻辑好像也没什么问题,网上大部分人也是这么写的。当较慢滑动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元素中的某一特性的表征量“,在异步回调中通过比较“异步回调生成点”和“异步回调执行点”此特征变量的值直接作出逻辑上的处理。

public class hardrefsimpleimageloadinglistener implements imageloadinglistener {

 public int identifier;

 public hardrefsimpleimageloadinglistener() {
 }

 public hardrefsimpleimageloadinglistener(int identifier) {
  this.identifier = identifier;
 }

 @override
 public void onloadingcancelled(string arg0, view arg1) {

 }

 @override
 public void onloadingcomplete(string arg0, view arg1, bitmap arg2) {

 }

 @override
 public void onloadingfailed(string arg0, view arg1, failreason arg2) {

 }

 @override
 public void onloadingstarted(string arg0, view view) {
 
 }
}

imageloader.getinstance().loadimage(imageurl, new hardrefsimpleimageloadinglistener(did) {
 @override
 public void onloadingcomplete(string imageuri, view view, bitmap loadedimage) {
  if (loadedimage != null) {
   if (identifier != did) {
    return;
   }
   imageview.setimagebitmap(loadedimage);
   // 其他业务逻辑处理..
  }
 }
});


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

 原文地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网