高效的加載圖片2.通過子線程(AsyncTask)加載圖片

上一篇我們說到如何以較小的內存,打開較大的圖片中使用了
BitmapFactory.decodeResource(res, resId, options);我們再主線程中執行了該方法,測試顯示沒有任何問題。我們的圖片資源是來自本地的文件,可是,如果我們的圖片來自sd卡甚至是來自網絡的資源,再在UI線程中使用該方法,那麼是很容易引起線程阻塞,導致ANR異常的(Application Not Responding)。
避免這個問題的解決方案,就是將圖片的加載過程放到子線程中執行,直到圖片加載完後,再通知主線程,讓主線程顯示這個圖片。
這篇文章就是教你如何在AsyncTask中,後臺加載圖片,並且處理併發的問題。
我們先說一下AsyncTask這個類。
這個類是安卓最簡單的調用子線程,在後臺完成任務,並且將結果返回到主線程的類。

AsyncTask<Integer, Void, Bitmap>

如何開始這個任務:new AsyncTask.execute(params);
在後臺執行的方法:doInBackground(Integer… params);
後臺方法執行完成後在主線程中執行的方法:onPostExecute(Bitmap bitmap)
要能夠很好的運用這個類,需要知道Integer, Void, Bitmap這三個參數分別的作用。如果你只是簡單的需要在後臺完成某項任務,無需任何UI操作,那三個參數都可以是void。
第一個參數是指定的輸入的類型,即在執行execute()方法時所輸入的參數,可以是一個或多個。這個參數會在doInBackground(Integer… params);中接收到。這裏面指定的是Integer類型。
第二個參數可指定發送進度更新需要的類型,這個參數會在onProgressUpdate(Void… values);中接收,在這個方法裏一般都是寫設置進度條進度的代碼。
它的工作方式如下:
在後臺線程中,我們從doInBackground()方法中調用publishProgress()方法。這樣onProgressUpdate()方法就能在UI線程上被調用。因此我們就可以在onProgressUpdate()方法中執行UI更新,但我們必須在doInBackground()方法中使用publishProgress()方法對它們進行控制。
第三個參數是指定返回值類型的。在doInBackground()方法中需要return一個值,在onPostExecute(Bitmap bitmap)方法中獲得的參數bitmap就是doInBackground()返回的值。而第三個參數就是指定這個值的類型的。

在某項複雜的場景中,我們需要能夠隨時能夠撤銷運行中的AsyncTask。比如ListView加載圖片時,當執行某個圖片加載任務時,要顯示的ImageView控件已經不見了,那當然不需要再執行這個任務了。於是我們就可以調用AsyncTask.cancel(boolean)方法。
撤銷AsyncTask任務有兩種模式,粗暴的和溫和的。如調用溫和的.cancel(false),該方法會設置isCancelled()的狀態爲true。隨後AsyncTask會檢查doInBackground()方法中的isCancelled()的狀態,然後選擇提前終止任務。
然而,如調用.cancel(true),它會直接終止doInBackground()方法所在的線程。如果可能,應該儘量避免使用此種方式。

http://blog.csdn.net/androidzhaoxiaogang/article/details/8579095
這篇博客是按照安卓開發文檔翻譯的,我實力有限,就不班門弄斧了。等看懂後再修改這篇博客。
http://m.blog.csdn.net/blog/domyself918/24260133
這篇博客也是安卓開發文檔的翻譯,但比較詳細。
http://blog.csdn.net/matrix_xu/article/details/8424038
這篇博客是講弱引用的用法和作用的,我覺的寫的很好。一起分享了。

2015/10/13
併發問題,有可能導致一個View同時加載兩多個圖片,並且因爲不能確定完成的先後順序,很有可能導致圖片加載錯誤。
看上面推薦的第二個博客後,覺的有所收穫,所以把收穫寫下來。
針對的主要是併發性問題的處理。當使用listView或GirdView這中循環使用view來加載圖片時。因爲無法確定加載圖片所用的時間,
1.可能在想要加載某個view的圖片時,這個view正在加載圖片了(因爲一個view的循環使用會導致它可能多次加載)。這時候需要取消正在加載的任務,而是讓新的任務開始加載。

public static boolean cancelPotentialWork(int data, ImageView imageView) {
//先獲得ImageView的正在加載,或者是已經加載了的task
    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

    if (bitmapWorkerTask != null) {
    //不爲null說明imageView正在加載,或是已經加載了一個task
    //獲得這個ImageView的ID
        final int bitmapData = bitmapWorkerTask.data;
        // 如果bitmap還沒有被設置或者bitmapdata數據和正在執行的data的不一樣
        if (bitmapData == 0 || bitmapData != data) {
            // 那麼就把之前的task取消掉
            bitmapWorkerTask.cancel(true);
        } else {
            //如果一樣那麼就是說當前的task就是image的task 就無需重新綁定了
            return false;
        }
    }
    // No task associated with the ImageView, or an existing task was cancelled
    return true;
}

2.還有一種情況是,當想要加載某個View時,發現這個view已經把視圖加載完了,這時候要判斷這個已經加載的圖片是不是自己想要加載的,如果是,則不用加載了,如果不是,應該重新加載圖片。
3.當然最後一種情況,當想要加載時,發現沒有任務在加載,view上也沒有加載圖片,所以正常加載就行了。

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