UrlImageViewHelper ( https://github.com/koush/UrlImageViewHelper ) に非常によく似たものを実装しようとしています。ここでは、単純な 1 行のコードを使用して簡単に URL から画像をロードし、画像が既にダウンロードされている場合代わりにキャッシュからロードされます。主な違いは、同じ効果が必要なことですが、URL からダウンロードする代わりに、独自のクライアント サーバー通信を使用して独自のサーバーから画像を受信したいということです。サーバー上のすべての画像は文字列で一意に識別でき、これを画像の ID として使用します。
私の主なアイデアはこれでした: LRU キャッシュを使用して画像を保持しますが、ビットマップ (非常に大きい) を保持する代わりに、生の画像データ バイナリを保持したいので、同じ画像を使用して異なるサイズのビットマップを構築できます。特定の状況に応じてオンデマンドの品質。
これはこれまでの私の実装です:
public class ImageHandler {
private static class BitmapCache extends LruCache<String, byte[]>
{
public WigoBitmapCache(int maxSize) {
super(maxSize);
}
@Override
protected int sizeOf(String key, byte[] value) {
return value.length;
}
}
private static class ImageHandlerThread extends Thread
{
/* THIS THREAD WILL DECODE THE IMAGE AND SET THE BITMAP TO THE IMAGEVIEW IN THE BACKGROUND */
Activity activity;
ImageView imageView;
byte[] imageBytes;
public ImageHandlerThread(Activity activity, ImageView imageView, byte[] imageBytes)
{
this.activity=activity;
this.imageView=imageView;
this.imageBytes=imageBytes;
}
public void run() {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o);
int factor1=o.outHeight/height;
int factor2=o.outWidth/width;
/* height and width are for now constant */
o = null;
o = new BitmapFactory.Options();
if (factor1>factor2)
o.inSampleSize=factor1;
else
o.inSampleSize=factor2;
Bitmap bit = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length,o);
setBitmap(bit);
bit = null;
}
private void setBitmap(final Bitmap bit) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bit);
}
});
}
}
private static class QueueItem
{ /*USED TO HOLD INFO ABOUT THE IMAGE REQUEST UNTIL THE IMAGE GETS FROM THE SERVER */
String imageName;
Activity activity;
ImageView imageView;
public QueueItem(String imageName, Activity activity, ImageView imageView)
{
this.imageName=imageName;
this.activity = activity;
this.imageView = imageView;
}
}
private BitmapCache cache; // this cache holds the image binaries
private ArrayList<QueueItem> queue; // this queue holds the info about the request, until the server sends the image
public ImageHandler(int maxSize)
{
cache=new BitmapCache(maxSize);
queue = new ArrayList<QueueItem>();
}
public synchronized void setBitmap(Activity activity, ImageView imageView, String imageName)
{
byte[] imageBytes = cache.get(imageName);
if (imageBytes==null)
{
QueueItem item = new QueueItem(imageName, activity, imageView);
queue.add(item);
/* HERE IS THE CODE TO RETRIEVE THE IMAGE BINARY FROM MY SERVER, THIS CODE WORKS FINE, SO THERE IS NO REASON TO BOHER YOU WITH IT */
}
else
{
ImageHandlerThread thread = new ImageHandlerThread(activity, imageView, imageBytes);
thread.start();
}
}
public synchronized void insert (String imageName, byte[] imageBytes)
{
/* THIS METHOD IS THE CALLBACK THAT IS CALLED WHEN THE IMAGE BINARY IS RECEIVED FROM THE SERVER */
cache.put(imageName, imageBytes);
for (QueueItem item: queue)
{
if (item.imageName.equals(imageName))
{
ImageHandlerThread thread = new ImageHandlerThread(item.activity, item.imageView, imageBytes);
thread.start();
queue.remove(item);
}
}
}
}
基本的に、ここでの主なメソッドは setBitmap() で、アクティビティ、ビットマップが必要な imageView、および画像名の名前を取得します。画像が既にキャッシュにある場合は、新しいスレッドが開始され、バイトが適切なサイズのビットマップにデコードされ、ビットマップが imageView に設定されます。画像がキャッシュに存在しない場合、画像が受信されるまでリクエストはキューに入れられ、サーバーから画像が取得されてから、以前と同じスレッドが開始されます。
これはすべて正常に機能します。問題は、imageView が画像の別のビットマップに設定されている場合、またはアクティビティが破棄されている場合でも、ビットマップがメモリに常駐し、GC によって収集されないことです。
最初は、アクティビティへの参照を保持していたためだと思いました。その参照はアクティビティを存続させますが、そうではないようです。アクティビティへの参照は非常に短命であり、画像がサーバーはこの参照をクリアします。
この実装ではメモリがすぐに不足してしまい、それを修正するために何をすべきか、またはその理由がわかりません。私が作成したビットマップは収集されていませんが、それらへの参照は保持していません。これは、画像をデコードする方法のアーティファクトでしょうか? または、スレッドは適切に収集されていない参照を保持していますか? 誰にもアイデアはありますか?