關於開源項目android--Imagedownloader的學習筆記

一個開源項目,關於圖像異步緩存下載的簡單apps,網上有相應的代碼,但是沒有任何說明和講解(英文blog中有,講了一下框架),那就自己研究吧。

    主要對ImageDownloader分析:

Bitmap downloadBitmap(String url);//從網站下載一幅圖片,比較簡單

      HttpClient

      AndroidHttpClient:Apache DefaultHttpClient的子類,已經配置好默認的合理設置和Android註冊過的方案。不能直接創建對象。

              AndroidHttpClient  newInstance(String userAgent,Context context)創建一個新HttpClient對象。

       HttpResponse:一個HTTP應答。

            HttpEntity getEntity(),獲得應答的消息實例

      HttpEntity:一個可以發送接收HTTP消息的實例。

             在一些情況下,javaDoc根據它們的內容來源把entity分三種:streamed,即內容來源是數據流,一般不可重複的;self-contained,內容來自內

            存也意味着和連接以及其他entities沒有關係,一般可重複;wrapping,內容從另一個entity獲得。

            InputStream  getContent():創建實例的一個新的InputStream對象 

            consumeContent():這個方法被調用意味着這個實例的內容不再被請求了,而該實例分配的所有資源都會被釋放。


  但是在 BitmapFactory.decodeStream 之前版本的bug可能會阻止代碼進行慢連接,而newFlushedInputStream(inputStream)方法可以解決這個問題。

  static class FlushedInputStream extends FilterInputStream;其中裏面的skip函數是爲了使在以後的讀輸入流時準確的跳過n個字節。

static class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                  int byte = read();//read()只讀一個字節,但返回0~255的隨機數
                  if (byte < 0) {
                      break;  //到文件結尾
                  } else {
                      bytesSkipped = 1; // 讀一個字節,這裏當跳過0個字節時爲什麼要讀一個字節不是太懂……
                  }
           }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
}

  如果在ListAdapter的getView方法中直接下載圖片,效果會很卡,因爲每一個新圖像的顯示都要等圖像下載下來

  非常遺憾的是,AndroidHttpClient竟然不能在主線程裏運行,否則會顯示"This thread forbids HTTP requests" 錯誤信息。對於AsyncTask類,它提供其中一個最簡單的方法來使新任務脫離UI線程運行。

  我們創建了一個ImageDownloader類來負責生成這些新的下載任務(task),它提供download方法來分配一張從URL下載的圖片到對應的ImageView中。

public class ImageDownloader {
    public void download(String url, ImageView imageView) {
            BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
            task.execute(url);
    }
}

  BitmapDownloaderTask是AsyncTask的子類,用來實際下載圖片的。調用execute(url)來運行它,能夠立即返回結果,這也是UI線程調用它的主要原因。

  其中doInBackground方法中調用了downloadBitmap來下載圖片,onPostExecute是當下載結束時UI線程調用的,將存儲在BitmapDownloaderTask中的imageView和下載的Bitmap相關聯,而這個ImageView是一個弱引用,可以被系統回收,所以要在onpostExecute中檢查弱引用和這個imageView不爲空。

class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private String url;
    private final WeakReference<ImageView> imageViewReference;

    public BitmapDownloaderTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    // Actual download method, run in the task thread
    protected Bitmap doInBackground(String... params) {
         // params comes from the execute() call: params[0] is the url.
         return downloadBitmap(params[0]);
    }
    @Override
    // Once the image is downloaded, associates it to the imageView
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            if (imageView != null) {BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
            // Change bitmap only if this process is still associated with this ImageView
            if (this == bitmapDownloaderTask) {
                imageView.setImageBitmap(bitmap);
            }
        }
        }
    }
}

對於ListView上的圖像瀏覽,當用戶快速滑動ListView時,某一個ImageView對象會被用到很多次,每一次顯示都會觸發一個下載,然後改變對應的圖片。和大多數並行應用一樣,有順序相關的問題。在這個程序中,不能保證下載會按開始的順序結束,有可能先開始的後下載完,“ The result is that the image finally displayed in the list may come from a previous item, which simply happened to have taken longer to download. ”  結果就是,由於與該item對應的網絡圖片下載慢,導致該item位置還暫時顯示着之前的item顯示的圖片(還沒被刷新,有可能長時間不被刷新

爲了解決這個問題,我們應該記住下載的順序,使得最後的下載會被有效地顯示,要讓每一個ImageView記住它們的上一次下載任務。因此我們給出了DownloadedDrawable類,向ImageView中加入對對應下載任務的弱引用來暫時綁定正在下載圖片的ImageView。

  static class DownloadedDrawable extends ColorDrawable ;//該類包含了一個對下載的弱引用

static class DownloadedDrawable extends ColorDrawable {   
   private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
        super(Color.BLACK);
        bitmapDownloaderTaskReference =
            new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
    }
    public BitmapDownloaderTask getBitmapDownloaderTask() {
        return bitmapDownloaderTaskReference.get();
   }
 }

上面ImageDownload中的download修改爲:

public void download(String url, ImageView imageView) {
     if (cancelPotentialDownload(url, imageView)) {
         BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
         DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
         imageView.setImageDrawable(downloadedDrawable);
         task.execute(url);
     }
}

private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageview);

//通過ImageView得到對應的下載

private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
            return downloadedDrawable.getBitmapDownloaderTask();
        }
    }
    return null;
}

下面總結下載過程:

    download(url , imageview)——>

          @創建一個和該imageview相對應的下載任務,這個任務對imageview進行弱引用

          @創建與這個任務相對應的DownloadedDrawable,對這個任務弱引用

          @imageview加載DownloadedDrawable

          @執行下載任務,下載對應url的圖像 ——execute(url)進入下載任務類

    在下載任務類中:

        ——>doInBackground  ===>downloadBitmap(url) 下載圖片,結果Bitmap作爲下面的參數

        ——>onPostExcute(Bitmap)

               @獲得任務引用的imageview

               @通過DownloadedDrawable獲得該imageview所對應的任務

               @如果當前任務是這個imageview所對應的任務,則設置這個imageview的圖片爲下載下

                來的Bitmap

imageview和任務相互弱引用,形成綁定關係



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