Android 圖片加載 BitmapFun

通過BitmapFun在項目中使用,結合代碼瞭解一下BitmapFun加載圖片的原理,以及最佳使用實踐。本文說明不包括BitmapFun的緩存部分。

Android開發在使用ListView和GridView時,可能會有很多網絡圖片需要加載,通常我們會爲每個圖片加載啓動一個Thread或者直接使用官方提供的AsyncTask,來做Http異步加載,但當每個ImageView子視圖都觸發一個AsyncTask來異步加載圖片時,這樣就會產生如下問題:


1. 當用戶快速滑動時,ImageView已經被回收,而綁定的線程還在運行,浪費CPU,浪費內存。

2. 無法確保當前視圖在結束時,分配的視圖已經進入循環隊列中給另外一個子視圖進行重用,意思就是,圖片顯示錯位了,不該顯示到當前問題的圖片卻顯示了,這個是經常遇到的問題。可以結合Adapter中的getView方法的convertView參數理解,ListView是回收和重複利用item的。

3. 無法確保所有的異步任務能夠按順序執行。


在這些問題下,官網給出的答案是,使用下面的方法來保證:

1. ImageView和Task綁定準確的加載對應圖片;

2. ImageView和Task無法對應時則取消任務;

不多說直接上代碼:

BitmapFun實現多線程併發加載圖片的原理:

一個類,兩個方法:

class AsyncDrawable extends BitmapDrawable{...}
 
boolean cancelPotentialWork(String url, ImageView imageView){...}
 
ImageViewResizeTask getBitmapWorkerTask(ImageView imageView){...}

具體代碼實現如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * 擴展BitmapDrawable類,通過弱引用關聯任務BitmapWorkerTask
 * BitmapDrawable被用來作爲佔位圖片,綁定任務到ImageView中
 */
private class AsyncDrawable extends BitmapDrawable {
    private final WeakReference<BitmapWorkerTask> viewResizeTaskReference;
 
    public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask viewResizeTask) {
        super(res, bitmap);
        viewResizeTaskReference = new WeakReference<BitmapWorkerTask>(viewResizeTask);
    }
 
    public BitmapWorkerTask getBitmapWorkerTask() {
        return viewResizeTaskReference.get();
    }
}
 
/**
 * 確保ImageView執行的是它對應的Task,否則取消任務
 * @param url
 * @param imageView
 * @return
 */
private boolean cancelPotentialWork(String url, ImageView imageView) { 
    // 獲得ImageView對應的Task
    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); 
   
    if (bitmapWorkerTask != null) { 
        final String imgUrl = bitmapWorkerTask.url; 
        if (imgUrl == null || !imgUrl.equals(url)) { 
            // Cancel previous task 
            bitmapWorkerTask.cancel(true); 
        } else 
            // The same work is already in progress 
            return false
        
    
    // No task associated with the ImageView, or an existing task was cancelled 
    return true
 
/**
 * 獲得已經被分配到ImageView的指定的Task
 * @param imageView
 * @return
 */
private BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
    if (imageView != null) {
        final Drawable drawable = imageView.getDrawable();
        if (drawable instanceof AsyncDrawable) {
            final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
            return asyncDrawable.getBitmapWorkerTask();
        }
    }
    return null;
}

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * 異步加載圖片
 * @param url
 * @param imageView
 */
private void loadBitmap(String url, ImageView imageView) { 
    if (cancelPotentialWork(url, imageView)) { 
        final BitmapWorkerTask task = new BitmapWorkerTask(imageView); 
        final AsyncDrawable asyncDrawable = new AsyncDrawable(context.getResources(), null, task); 
        imageView.setImageDrawable(asyncDrawable); 
        task.execute(url); 
    
 
/*
 * 異步加載圖片Task類
 */
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
 
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }
 
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
            if (this == bitmapWorkerTask && imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

 

最佳使用實踐,提高流暢度

1. 設置ListView的OnScrollListener事件,在滑動的時候不加載圖片

1
2
3
4
5
6
7
8
9
public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
        if (!Utils.hasHoneycomb()) {
            mImageFetcher.setPauseWork(true);
        }
    } else {
        mImageFetcher.setPauseWork(false);
    }
}

2. 在Activity或Fragment的onResume(),onPause(),onDestroy()方法中調用恰當方法非常有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void onResume() {
    super.onResume();
    mImageFetcher.setExitTasksEarly(false);
}
 
@Override
public void onPause() {
    super.onPause();
    mImageFetcher.setPauseWork(false);
    mImageFetcher.setExitTasksEarly(true);
    mImageFetcher.flushCache();
}
 
@Override
public void onDestroy() {
    super.onDestroy();
    mImageFetcher.closeCache();
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章