Android非同步回調中的UI同步性問題分析_Android

來源:互聯網
上載者:User

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,是非常必要且有效手段。

 原文地址:http://www.cnblogs.com/lwbqqyumidi/p/4110377.html

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.