Android圖片異步加載


開發Android程序,一般情況下都會有兩個操作,圖片的異步加載與緩存,而圖片的異步加載大都是從網絡讀取圖片(還有生成本地圖片縮略圖等操作),為了減少網絡操作,加快圖片加載速度就需要對圖片進行緩存,所以網上的好多圖片異步加載方法都是與圖片的緩存緊密關聯的。但也有可能用戶已經有了緩存的相關類庫,這樣使用起來就會有點麻煩。

最近一段處理跟圖片相關的問題,本來是自己寫的圖片加載,不過有些狀態的控制還是比較煩人的,比如ListView滾動時ImageView的重用,所以本着偷懶與充分利用現有資源的態度去網上搜羅圖片異步加載的代碼,最終在GreenDroid UI庫中找到一個,其中有個AsyncImageView的自定義View用於異步加載圖片,不過也像網上的大多數圖片異步加載方法一樣,是跟圖片的緩存關聯在一起的,不過只是很簡單的內存緩存,無文件緩存。圖片的加載方法也如其他的一樣是寫死了的,這就限制了其使用范圍,只可通過InputStream來decode圖片,而像生成縮略圖或其他一些圖片處理的異步處理就無法用途。修改現有類庫總比自己從頭寫來的簡單,於是稍微修改了下AsyncImageView,使其可以自定義緩存與圖片加載方法,對於AsyncImageView只有一點點的修改,大都是別人源碼。

1. 核心類

  • ImageLoader:圖片加載核心類,內部使用線程池加載圖片
  • ImageRequest:表示一個圖片加載的請求
  • AsyncImageView:自定義的圖片異步加載View
  • LoadMethod:自定義圖片加載方法的接口,可以通過實現此接口來自定義圖片的加載方法
  • CacheCallback:緩存接口,可以通過實現此接口實現對緩存的讀寫
  • AsyncImageView.OnImageViewLoadListener:圖片加載狀態監聽(開始,失敗,結束)

2. 圖片加載方法

public void run() {

    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

    final Handler h = mHandler;
    Bitmap bitmap = null;
    Throwable throwable = null;

    h.sendMessage(Message.obtain(h, ON_START));

    try {
        if (TextUtils.isEmpty(mUrl)) {
            throw new Exception("The given URL cannot be null or empty");
        }

        // 如果自定義了加載方法,則用自定義的方法
        if (mLoadMethod != null) {
            bitmap = mLoadMethod.load(mUrl);
        } else {

            InputStream inputStream = null;

            // Asset
            if (mUrl.startsWith("file:///android_asset/")) {
                inputStream = sAssetManager.open(mUrl.replaceFirst(
                        "file:///android_asset/", ""));
            }
            // File
            else if (mUrl.startsWith("file:///") || mUrl.startsWith("/")) {
                if (mUrl.startsWith("file:///"))
                    mUrl = mUrl.replaceFirst("file:///", "/");
                inputStream = new FileInputStream(mUrl);
            }
            // NetWork
            else {
                // 在用URL類加載圖片時,發現有的機型上面通過URL類獲得的InputStream解析獲得的圖片總是null,故使用HttpClient
                HttpGet httpRequest = new HttpGet(mUrl);
                HttpClient httpclient = new DefaultHttpClient();
                HttpParams httpParams = new BasicHttpParams();
                HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
                HttpConnectionParams.setSoTimeout(httpParams, 5000);
                httpRequest.setParams(httpParams);
                HttpResponse response = (HttpResponse)httpclient.execute(httpRequest);
                HttpEntity entity = response.getEntity();
                BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
                InputStream instream = bufHttpEntity.getContent();
                BufferedInputStream bi = new BufferedInputStream(instream);
                inputStream = bi;
            }

            // 雖然AsyncImageView中有設置BitmapFactory.Options的方法,但一般情況下都未知圖片的大小,也就無法計算相應的inSampleSize,
            // 也就無法設置相應的BitmapFactory.Options,所以一般情況下還是根據自己的需要自定義LoadMethod為好
            bitmap = BitmapFactory.decodeStream(inputStream, null,
                    (mOptions == null) ? sDefaultOptions : mOptions);
            inputStream.close();
        }
        if (mBitmapProcessor != null && bitmap != null) {
            final Bitmap processedBitmap = mBitmapProcessor.processImage(bitmap);
            if (processedBitmap != null) {
                bitmap.recycle();
                bitmap = processedBitmap;
            }
        }

    } catch (Exception e) {
        Log.e(LOG_TAG, "Error while fetching image", e);
        throwable = e;
    }

    if (bitmap == null) {
        if (throwable == null) {
            throwable = new Exception("Skia image decoding failed");
        }
        h.sendMessage(Message.obtain(h, ON_FAIL, throwable));
    } else {
        h.sendMessage(Message.obtain(h, ON_END, bitmap));
        if (mCache != null) {
            mCache.writeCache(TextUtils.isEmpty(mCacheKey) ? mUrl : mCacheKey, bitmap);
        }
    }
}

如果自定義了LoadMethod,會調用相應的方法加載圖片,如果沒有自定義,會使用默認的加載方法,可以加載本地圖片,Asset圖片與網絡圖片,GreenDroid的源碼中加載網絡圖片是用的URL的,但我們以前在加載網絡圖片時遇到一個問題,有的機型通過URL類獲得的ImputStream解析圖片總是返回null,所以就改為了HttpClient。

3.使用方法

通過AsyncImageView的setPath方法來加載圖片,setPath有3個重載方法:

  • public void setPath(String path)
  • public void setPath(String path, LoadMethod loadMethod)
  • public void setPath(String path, LoadMethod loadMethod, String cacheKey)

第一個參數指定要加載的圖片的路徑,第二個參數為自定義的圖片加載方法,若不指定則用默認的。

至於加第三個參數,是做緩存用的,一般要加載的圖片的路徑都是唯一的,所以一般用第一個參數來做為緩存的Key就行了,但也有特殊情況,比如讀取局域網中的圖片,一般都是自動獲取IP,所以根據圖片路徑做為緩存的Key可能是不合適的,所以就需要根據需要手動指定用來作為緩存的Key。

/**
     * 設置要加載的圖片的路徑, 可為網絡路徑, Asset文件路徑(file:///android_asset), 本地圖片路徑(file:///或/)
     * 
     * @param path 要加載的圖片的路徑, 若為null則加載默認圖片
     * @param loadMethod 自定義的圖片加載的方法, 可以null, 使用默認的加載方法
     * @param cacheKey 緩存key
     */
    public void setPath(String path, LoadMethod loadMethod, String cacheKey) {

        // Check the url has changed
        if (mBitmap != null && path != null && path.equals(mUrl)) { // TODO mBitmap != null necessary?
            return;
        }

        stopLoading();
        mUrl = path;
        mCacheKey = cacheKey;
        mLoadMethod = loadMethod;

        // Setting the url to an empty string force the displayed image to the
        // default image
        if (TextUtils.isEmpty(mUrl)) {
            mBitmap = null;
            setDefaultImage();
        } else {
            if (!mPaused) {
                reload();
            } else {
                // We're paused: let's look in a synchronous and efficient cache
                // prior using the default image.
                mBitmap = readCache(); // TODO 可能會耗時間
                if (mBitmap != null) {
                    setImageBitmap(mBitmap);
                } else {
                    setDefaultImage();
                }
            }
        }
    }
public void reload(boolean force) {
        if (mRequest == null && mUrl != null) {

            // Prior downloading the image ... let's look in a cache !
            mBitmap = null;
            if (!force) {
                // This may take a long time.
                mBitmap = readCache();
            }

            if (mBitmap != null) {
                setImageBitmap(mBitmap);
                return;
            }

            setDefaultImage();
            mRequest = new ImageRequest(mUrl, this, mImageProcessor, mOptions, mCacheKey);
            mRequest.load(getContext(), mLoadMethod);
            if (ImageLoader.getInstance() != null && ImageLoader.getInstance().getCache() == null) {
                ImageLoader.getInstance().setCache(mCache);
            }
        }

readCache()用於讀取緩存,代碼如下:

    private Bitmap readCache() {
        if (mCache != null)
            return mCache.readCache(TextUtils.isEmpty(mCacheKey) ? mUrl : mCacheKey);
        return null;
    }

其中的mCache由用戶能過setCacheCallback(CacheCallback callback)設置用戶自定義的緩存方法,由此將圖片的加載與緩存分離開,使用戶可以使用現有的緩存實現。如要用戶指定了緩存Key就使用用戶指定的Key,否則就用圖片的路徑作Key。

4.AsyncImageView中的其他重要方法

  • reload([boolean force]):重新加載
  • stopLoading():停止加載,如果當前正在加載則沒有效果,如果加載任務在加載線程池隊列中則取消。
  • setDefaultImage...()類方法:設置默認圖片。
  • setPause(boolean pause):是否加載圖片

 

源碼下載:AsyncImage


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM