Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android資訊 >> Android ImageLoader框架之圖片加載與加載策略

Android ImageLoader框架之圖片加載與加載策略

編輯:Android資訊

在Android ImageLoader框架之初始配置與請求調度中,我們已經講述了ImageLoader的請求配置與調度相關的設計與實現。今天我們就來深入了解圖片的具體加載過程以及加載的策略(包括按順序加載和逆序加載) ,在這其中我會分享我的一些設計決策,也歡迎大家給我提建議。

圖片的加載

Loader與LoaderManager的實現

在上一篇文章Android ImageLoader框架之初始配置與請求調度中,我們聊到了Loader與LoaderManager。 ImageLoader不斷地從隊列中獲取請求,然後解析到圖片uri的schema,從schema的格式就可以知道它是存儲在哪裡的圖片。例如網絡圖片對象的schema是http或者https,sd卡存儲的圖片對應的schema為file,schemae與Loader有一個對應關系。根據schema我們從LoaderManager中獲取對應的Loader來加載圖片。這個設計保證了SimpleImageLoader可加載圖片類型的可擴展性,這就是為什麼會增加loader這個包的原因。用戶只需要根據uri的格式來構造圖片uri,並且實現自己的Loader類,然後將Loader對象注入到LoaderManager即可。RequestDispatcher中的run函數如下 :

 @Override
    public void run() {
        try {
            while (!this.isInterrupted()) {
                final BitmapRequest request = mRequestQueue.take();
                if (request.isCancel) {
                    continue;
                }

                final String schema = parseSchema(request.imageUri);
                // 根據schema獲取loader
                Loader imageLoader = LoaderManager.getInstance().getLoader(schema);
                imageLoader.loadImage(request);
            }
        } catch (InterruptedException e) {
            Log.i("", "### 請求分發器退出");
        }
    }

Loader只定義了一個接口,只用一個加載圖片的方法。

public interface Loader {
    public void loadImage(BitmapRequest result);
}

抽象是為了可擴展,定義這個接口,我們就可以注入自己的圖片加載實現類。例如從資源、assets中加載。不管從網絡還是本地加載圖片,我們加載圖片的過程有如下幾個步驟:

  1. 判斷緩存中是否含有該圖片;
  2. 如果有則將圖片直接投遞到UI線程,並且更新UI;
  3. 如果沒有緩存,則從對應的地方獲取到圖片,並且將圖片緩存起來,然後再將結果投遞給UI線程,更新UI;

我們可以發現,不管從哪裡加載圖片,這些邏輯都是通用的,因此我抽象了一個AbsLoader類。它將這幾個過程抽象起來,只將變化的部分交給子類處理,就相當於AbsLoader封裝了一個邏輯框架( 可以思考用了什麼設計模式),大致代碼如下 :

/**
 * @author mrsimple
 */
public abstract class AbsLoader implements Loader {

    /**
     * 圖片緩存
     */
    private static BitmapCache mCache = SimpleImageLoader.getInstance().getConfig().bitmapCache;

    @Override
    public final void loadImage(BitmapRequest request) {
        // 1、從緩存中獲取
        Bitmap resultBitmap = mCache.get(request);
        Log.e("", "### 是否有緩存 : " + resultBitmap + ", uri = " + request.imageUri);
        if (resultBitmap == null) {
            showLoading(request);
            // 2、沒有緩存,調用onLoaderImage加載圖片
            resultBitmap = onLoadImage(request);
            // 3、緩存圖片
            cacheBitmap(request, resultBitmap);
        } else {
            request.justCacheInMem = true;
        }
        // 4、將結果投遞到UI線程
        deliveryToUIThread(request, resultBitmap);
    }

    /** 加載圖片的hook方法,留給子類處理
     * @param request
     * @return
     */
    protected abstract Bitmap onLoadImage(BitmapRequest request);
    // 代碼省略
    }

代碼邏輯如上所述實現了一個模板函數,變化的部分就是onLoadImage,子類在這裡實現真正的加載圖片的方法。比如從網絡上加載圖片。

/**
 * @author mrsimple
 */
public class UrlLoader extends AbsLoader {

    @Override
    public Bitmap onLoadImage(BitmapRequest request) {
        final String imageUrl = request.imageUri;
        FileOutputStream fos = null;
        InputStream is = null;
        try {
            URL url = new URL(imageUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(conn.getInputStream());
            is.mark(is.available());

            final InputStream inputStream = is;
            BitmapDecoder bitmapDecoder = new BitmapDecoder() {

                @Override
                public Bitmap decodeBitmapWithOption(Options options) {
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
                    //
                    if (options.inJustDecodeBounds) {
                        try {
                            inputStream.reset();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    } else {
                        // 關閉流
                        conn.disconnect();
                    }
                    return bitmap;
                }
            };

            return bitmapDecoder.decodeBitmap(request.getImageViewWidth(),
                    request.getImageViewHeight());
        } catch (Exception e) {

        } finally {
            IOUtil.closeQuietly(is);
            IOUtil.closeQuietly(fos);
        }

        return null;
    }

}

在初始化ImageLoader時我們會默認將幾個Loader注入到LoaderManager中,然後在加載圖片時ImageLoader會根據圖片的schema來獲取對應Loader來完成加載功能。

    /**
     * 
     */
    private LoaderManager() {
        register(HTTP, new UrlLoader());
        register(HTTPS, new UrlLoader());
        register(FILE, new LocalLoader());
    }

加載策略

加載策略就是你的圖片加載請求提交以後ImageLoader按照一個什麼規則來加載你的請求。默認就是SerialPolicy策略(FIFO),誰在隊列前面就是誰優先被執行。但是事情往往沒有那麼簡單,我們在ListView滾動時,我們希望最後添加到請求隊列的圖片優先得了加載,因此此時它們就在手機屏幕上,所以我們又添加了一個ReversePolicy策略。咦,對於這種存在各種可能性的部分,我們最不能具體化,還是要抽象!於是我定義了LoadPolicy接口,它的作用是compare兩個請求,以此來規定排序原則。

public interface LoadPolicy {
    public int compare(BitmapRequest request1, BitmapRequest request2);
}

因為我們的請求隊列使用的是優先級隊列PriorityBlockingQueue,因此我們的BitmapRequest都實現了 Comparable 接口,我們在BitmapRequest的函數中將compareTo委托給LoadPolicy對象的compare。

    @Override
    public int compareTo(BitmapRequest another) {
        return mLoadPolicy.compare(this, another);
    }

我們看看默認的加載策略,即按順序加載,先添加到隊列的請求先被執行。

/**
 * 順序加載策略
 * 
 * @author mrsimple
 */
public class SerialPolicy implements LoadPolicy {

    @Override
    public int compare(BitmapRequest request1, BitmapRequest request2) {
        // 那麼按照添加到隊列的序列號順序來執行
        return request1.serialNum - request2.serialNum;
    }

}

逆序加載則為 :

/**
 * 逆序加載策略,即從最後加入隊列的請求進行加載
 * 
 * @author mrsimple
 */
public class ReversePolicy implements LoadPolicy {

    @Override
    public int compare(BitmapRequest request1, BitmapRequest request2) {
        // 注意Bitmap請求要先執行最晚加入隊列的請求,ImageLoader的策略
        return request2.serialNum - request1.serialNum;
    }
}

呵,想想這不是策略模式麼!原來模式無處不在,當你習慣之後你就會發現模式在無形之中已經運用到你的代碼了。如上所示,策略都是簡單的實現,這個策略只需要在配置ImageLoader時指定就行了,用戶也可以根據自己的需求來實現策略類,並且注入給ImageLoader。這樣就保證了靈活性、可擴展性。

總結

通過Loader和LoaderManager保證了可加載圖片來源的擴展性,即圖片可以存儲在網絡上、sd卡中、res文件夾中等等,實現一個從特定位置加載圖片的Loader,然後給這個Loader注冊一個schema,在加載圖片的時候根據圖片的路徑獲取schema,再通過schema獲取Loader,通過Loader加載圖片。

而圖片的加載策略又通過LoadPolicy這個抽象來定制,用戶可以自行實現加載策略。這樣就保證了靈活性,當然還有後期的圖片緩存也是需要同樣的靈活性。和我在公共技術點之面向對象六大原則所說,面向對象的幾大原則最終化為幾個簡單的關鍵字: : 抽象、單一職責、最小化。領悟到了這些思想,我想你的代碼質量應該會有一個質的提升。

ImageLoader庫,圖片緩存肯定必不可少。關於圖片的緩存設計,還是那句老話,待我下回講解~

Github地址

SimpleImageLoader。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved