Android非同期コールバックにおけるUI同期性の問題
10174 ワード
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同期性の問題」はしばしば存在し、少し注意しないと理解しにくいバグが発生し、非同期特性の存在によりランダム性がある場合がある.いくつかのニーズの複雑さのため、このようなバグは隠蔽性が強く、無視されやすい場合があります.少なくともこれまで、実際の開発では、本人がこのような問題に遭遇したのはいくつかあった.
純粋な文字の説明はよく理解できないかもしれませんが、以下では、一般的に使用されているAndroid-Universal-Image-Loaderを例に、潜在的に存在する「Android非同期コールバックUI同期性の問題」を簡単に例に挙げます.
ListView Item ViewにはImageViewがあり、Android-Universal-Image-Loaderで表示をロードします.画像のロードが完了したら、論理処理(画像のロード進捗バーを隠すなど)を行う必要があります.通常のコードは次のとおりです.
最初はコードロジックも問題ないように見えましたが、ネット上のほとんどの人もそう書いています.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要素の特性の特性評価量」を把握し、非同期コールバックで「非同期コールバック生成ポイント」と「非同期コールバック実行ポイント」という特徴変数の値を比較することで、論理的な処理を直接行います.
要するに、このような「Android非同期コールバックUI同期性の問題」は、不要なバグが発生しないように、「非同期コールバック生成点」と「非同期コールバック実行点」の特徴変数の値を比較して論理的に処理することが望ましい.
問題の説明を容易にするために、ここではまず、「Android非同期コールバックUI同期性問題」について、非同期コールバック実行時(非同期コールバック実行ポイントと呼ぶ)に、現在のUIインタフェース上の要素が、この非同期コールバックを最初に生成した呼び出し器が実行を開始したとき(非同期コールバック生成ポイントと呼ばれる)のUI要素と一致せず、UI要素の可能なインタフェースの変化だけでなく、UI要素インタフェースおよびコンテンツがまだ変化していない場合でも、「非同期コールバック実行ポイント」および「非同期コールバック生成ポイント」のUI要素の特性の特徴付け量(現在のUI要素を特徴付けるフィールド値など)が関連している可能性があります.
符号化の過程で「Android非同期コールバックUI同期性の問題」はしばしば存在し、少し注意しないと理解しにくいバグが発生し、非同期特性の存在によりランダム性がある場合がある.いくつかのニーズの複雑さのため、このようなバグは隠蔽性が強く、無視されやすい場合があります.少なくともこれまで、実際の開発では、本人がこのような問題に遭遇したのはいくつかあった.
純粋な文字の説明はよく理解できないかもしれませんが、以下では、一般的に使用されている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 @Override 12 public void onLoadingStarted(String imageUri, View view) { 13
14 } 15
16 @Override 17 public void onLoadingCancelled(String arg0, View arg1) { 18
19 } 20
21 @Override 22 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 @Override 13 public void onLoadingCancelled(String arg0, View arg1) { 14
15 } 16
17 @Override 18 public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) { 19
20 } 21
22 @Override 23 public void onLoadingFailed(String arg0, View arg1, FailReason arg2) { 24
25 } 26
27 @Override 28 public void onLoadingStarted(String arg0, View view) { 29
30 } 31 } 32
33 ImageLoader.getInstance().loadImage(imageUrl, new HardRefSimpleImageLoadingListener(did) { 34 @Override 35 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同期性の問題」は、不要なバグが発生しないように、「非同期コールバック生成点」と「非同期コールバック実行点」の特徴変数の値を比較して論理的に処理することが望ましい.