日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

高效使用Bitmaps(二) 后台加载Bitmap

發(fā)布時間:2025/7/14 63 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高效使用Bitmaps(二) 后台加载Bitmap 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)自:http://my.oschina.net/rengwuxian/blog/183802

為什么要在后臺加載Bitmap?

在Android中,使用BitmapFactory.decodeResource(), BitmapFactory.decodeStream() 等方法可以把圖片加載到Bitmap中。但由于這些方法是耗時的,所以多數(shù)情況下,這些方法應(yīng)該放在非UI線程中,否則將有可能導致界面的卡頓,甚至是觸 發(fā)ANR。

一般情況下,網(wǎng)絡(luò)圖片的加載必須放在后臺線程中;而本地圖片就可以根據(jù)實際情況自行決定了,如果圖片不多不大的話,也可以在UI線程中操作來圖個方便。至于谷歌官方的說法,是只要是從硬盤或者從網(wǎng)絡(luò)加載Bitmap,統(tǒng)統(tǒng)不應(yīng)該在主線程中進行。

基礎(chǔ)操作:使用AsyncTask

?

01class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
02????private final WeakReference<ImageView> imageViewReference;
03????private int data = 0;
04?
05????public BitmapWorkerTask(ImageView imageView) {
06????????// Use a WeakReference to ensure the ImageView can be garbage collected
07????????imageViewReference = new WeakReference<ImageView>(imageView);
08????}
09?
10????// Decode image in background.
11????@Override
12????protected Bitmap doInBackground(Integer... params) {
13????????data = params[0];
14????????return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
15????}
16?
17????// Once complete, see if ImageView is still around and set bitmap.
18????@Override
19????protected void onPostExecute(Bitmap bitmap) {
20????????if (imageViewReference != null && bitmap != null) {
21????????????final ImageView imageView = imageViewReference.get();
22????????????if (imageView != null) {
23????????????????imageView.setImageBitmap(bitmap);
24????????????}
25????????}
26????}
27}

以上代碼摘自Android官方文檔,是一個后臺加載Bitmap并在加載完成后自動將Bitmap設(shè)置到ImageView的AsyncTask的實現(xiàn)。有了這個AsyncTask之后,異步加載Bitmap只需要下面的簡單代碼:

?

1public void loadBitmap(int resId, ImageView imageView) {
2????BitmapWorkerTask task = new BitmapWorkerTask(imageView);
3????task.execute(resId);
4}

然后,一句loadBitmap(R.id.my_image, mImageView) 就能實現(xiàn)本地圖片的異步加載了。

并發(fā)操作:在ListView和GridView中進行后臺加載

在實際中,影響性能的往往是ListView和GridView這種包含大量圖片的控件。在滑動過程中,大量的新圖片在短時間內(nèi)一起被加載,對于沒有進行任何優(yōu)化的程序,卡頓現(xiàn)象必然會隨之而來。通過使用后臺加載Bitmap的方式,這種問題將被有效解決。具體怎么做,我們來看看谷歌推薦的方法。

首先創(chuàng)建一個實現(xiàn)了Drawable接口的類,用來存儲AsyncTask的引用。在本例中,選擇了繼承BitmapDrawable,用來給ImageView設(shè)置一個預(yù)留的占位圖,這個占位圖用于在AsyncTask執(zhí)行完畢之前的顯示。

?

01static class AsyncDrawable extends BitmapDrawable {
02????private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
03?
04????public AsyncDrawable(Resources res, Bitmap bitmap,
05????????????BitmapWorkerTask bitmapWorkerTask) {
06????????super(res, bitmap);
07????????bitmapWorkerTaskReference =
08????????????new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
09????}
10?
11????public BitmapWorkerTask getBitmapWorkerTask() {
12????????return bitmapWorkerTaskReference.get();
13????}
14}

接下來,和上面類似,依然是使用一個loadBitmap()方法來實現(xiàn)對圖片的異步加載。不同的是,要在啟動AsyncTask之前,把AsyncTask傳給AsyncDrawable,并且使用AsyncDrawable為ImageView設(shè)置占位圖:

?

1public void loadBitmap(int resId, ImageView imageView) {
2????if (cancelPotentialWork(resId, imageView)) {
3????????final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
4????????final AsyncDrawable asyncDrawable =
5????????????????new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
6????????imageView.setImageDrawable(asyncDrawable);
7????????task.execute(resId);
8????}
9}

然后在Adapter的getView()方法中調(diào)用loadBitmap()方法,就可以為每個Item中的ImageView進行圖片的動態(tài)加載了。

loadBitmap()方法的代碼中有兩個地方需要注意:第一,cancelPotentialWork()這個方法,它的作用是進行兩項檢查:首先檢查當前是否已經(jīng)有一個AsyncTask正在為這個ImageView加載圖片,如果沒有就直接返回true。如果有,再檢查這個Task正在加載的資源是否與自己正要進行加載的資源相同,如果相同,那就沒有必要再進行多一次的加載了,直接返回false;而如果不同(為什么會不同?文章最后會有解釋),就取消掉這個正在進行的任務(wù),并返回true。第二個需要注意的是,本例中的?BitmapWorkerTask?實際上和上例是有所不同的。這兩點我們分開說,首先我們看cancelPotentialWork()方法的代碼:

?

01public static boolean cancelPotentialWork(int data, ImageView imageView) {
02????final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
03?
04????if (bitmapWorkerTask != null) {
05????????final int bitmapData = bitmapWorkerTask.data;
06????????if (bitmapData != data) {
07????????????// 取消之前的任務(wù)
08????????????bitmapWorkerTask.cancel(true);
09????????} else {
10????????????// 相同任務(wù)已經(jīng)存在,直接返回false,不再進行重復(fù)的加載
11????????????return false;
12????????}
13????}
14????// 沒有Task和ImageView進行綁定,或者Task由于加載資源不同而被取消,返回true
15????return true;
16}

在cancelPotentialWork()的代碼中,首先使用getBitmapWorkerTask()方法獲取到與ImageView相關(guān)聯(lián)的Task,然后進行上面所說的判斷。好,我們接著來看這個getBitmapWorkerTask()是怎么寫的:

?

01private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
02???if (imageView != null) {
03???????final Drawable drawable = imageView.getDrawable();
04???????if (drawable instanceof AsyncDrawable) {
05???????????final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
06???????????return asyncDrawable.getBitmapWorkerTask();
07???????}
08????}
09????return null;
10}

從代碼中可以看出,該方法通過imageView獲取到與它內(nèi)部的 Drawable對象,如果獲取到了并且該對象為AsyncDrawable的實例,就調(diào)用這個AsyncDrawable的 getBitmapWorkerTask()方法來獲取到它對應(yīng)的Task,也就是通過一個 ImageView->Drawable->AsyncTask的鏈來獲取到ImageView所對應(yīng)的AsyncTask。

好的,cancelPotentialWork()方法分析完了,我們回到剛才提到的第二個點:BitmapWorkerTask類的不同。這個類的改動在于onPostExecute()方法,具體請看下面代碼:

?

01class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
02????...
03?
04????@Override
05????protected void onPostExecute(Bitmap bitmap) {
06????????if (isCancelled()) {
07????????????bitmap = null;
08????????}
09?
10????????if (imageViewReference != null && bitmap != null) {
11????????????final ImageView imageView = imageViewReference.get();
12????????????final BitmapWorkerTask bitmapWorkerTask =
13????????????????????getBitmapWorkerTask(imageView);
14????????????if (this == bitmapWorkerTask && imageView != null) {
15????????????????imageView.setImageBitmap(bitmap);
16????????????}
17????????}
18????}
19}

從代碼中可以看出,在后臺加載完Bitmap之后,它 并不是直接把Bitmap設(shè)置給ImageView,而是先判斷這個ImageView對應(yīng)的Task是不是自己(為什么會不同?文章最后會有解釋)。如果是自己,才會執(zhí)行ImageView的setImageBitmap()方法。到此,一個并發(fā)的異步加載ListView(或GridView)中圖片的實現(xiàn)全部完成。

延伸:文中兩個“為什么會不同”的解答

首先,簡單說一下ListView中Item和Item對應(yīng)的View的關(guān)系 (GridView中同理)。假設(shè)一個ListView含有100項,那么它的100個Item應(yīng)該分別對應(yīng)一個View用于顯示,這樣一共是100個 View。但Android實際上并沒有這樣做。出于內(nèi)存考慮,Android只會為屏幕上可見的每個Item分配一個View。用戶滑動 ListView,當?shù)谝粋€Item移動到在可視范圍外后,他所對應(yīng)的View將會被系統(tǒng)分配給下一個即將出現(xiàn)的Item。

回到問題。

我們不妨假設(shè)屏幕上顯示了一個ListView,并且它最多能顯示10個 Item,而用戶在最頂部的Item(不妨稱他為第1個Item)使用Task加載Bitmap的時候進行了滑動,并且直到第1個Item消失而第11個 Item已經(jīng)在屏幕底部出現(xiàn)的時候,這個Task還沒有加載完成。那么此時,原先與第1個Item綁定的ImageView已經(jīng)被重新綁定到了第11個 Item上,并且第11個Item觸發(fā)了getItem()方法。在getItem()方法中,ImageView第二次使用Task為自己加載Bitmap,但這時它需要加載的圖片資源已經(jīng)變了(由第1個Item對應(yīng)的資源變成了第11個Item對應(yīng)的資源),因此在cancelPotentialWork()方法執(zhí)行時會判斷兩個資源不一致。這就是為什么相同ImageView卻對應(yīng)了不同的資源。

同理,一個Task持有了一個ImageView,但由于這個Task有可能已經(jīng)過時,因此這個ImageView所對應(yīng)的Task未必就是這個Task本身,也有可能是另一個更年輕的Task。

轉(zhuǎn)載于:https://www.cnblogs.com/xingfuzzhd/p/3470603.html

總結(jié)

以上是生活随笔為你收集整理的高效使用Bitmaps(二) 后台加载Bitmap的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。