bitmap緩存
Ⅰ android lrucache除了緩存bitmap還能緩存其他數據類型嗎
LruCache<String, Bitmap> memCache = new LruCache<String, Bitmap>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
// TODO Auto-generated method stub
return value.getRowBytes() * value.getHeight();
}
};
BitMap只是一個類,應該都支持的。問題是你怎麼獲取大小?
Ⅱ 安卓開發Xutils.Bitmap怎麼實現的三級緩存
網路緩存
網路拉取圖片嚴格來講不能稱之為緩存,實質上就是下載url對應的圖片,我們這里姑且把它看作是緩存的一種。仿照BitmapUtil中的display方法,我自己定製的CustomBitmapUtils也定義這個方法,根據傳入的url,將圖片設置到ivPic控制項上。
[java] view plain
public void display(ImageView ivPic, String url) {
}
定義網路緩存的工具類,在訪問網路的時候,我使用了AsyncTask來實現,在AsyncTask的doInBackGround方法里下載圖片,然後將 圖片設置給ivPic控制項,AsyncTask有三個泛型,其中第一個泛型是執行非同步任務的時候,通過execute傳過來的參數,第二個泛型是更新的進度,第三個泛型是非同步任務執行完成之後,返回來的結果,我們這里返回一個Bitmap。具體的下載實現代碼如下:
[java] view plain
<pre name="code" class="java">/**
* 網路緩存的工具類
*
* @author ZHY
*
*/
public class NetCacheUtils {
private LocalCacheUtils localCacheUtils;
private MemoryCacheUtils memoryCacheUtils;
public NetCacheUtils() {
localCacheUtils = new LocalCacheUtils();
memoryCacheUtils = new MemoryCacheUtils();
}
/**
* 從網路下載圖片
*
* @param ivPic
* @param url
*/
public void getBitmapFromNet(ImageView ivPic, String url) {
// 訪問網路的操作一定要在子線程中進行,採用非同步任務實現
MyAsyncTask task = new MyAsyncTask();
task.execute(ivPic, url);
}
/**
* 第一個泛型--非同步任務執行的時候,通過execute傳過來的參數; 第二個泛型--更新進度; 第三個泛型--非同步任務執行以後返回的結果
*
* @author ZHY
*
*/
private class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {
private ImageView ivPic;
private String url;
// 耗時任務執行之前 --主線程
@Override
protected void onPreExecute() {
super.onPreExecute();
}
// 後台執行的任務
@Override
protected Bitmap doInBackground(Object... params) {
// 執行非同步任務的時候,將URL傳過來
ivPic = (ImageView) params[0];
url = (String) params[1];
Bitmap bitmap = downloadBitmap(url);
// 為了保證ImageView控制項和URL一一對應,給ImageView設定一個標記
ivPic.setTag(url);// 關聯ivPic和URL
return bitmap;
}
// 更新進度 --主線程
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
// 耗時任務執行之後--主線程
@Override
protected void onPostExecute(Bitmap result) {
String mCurrentUrl = (String) ivPic.getTag();
if (url.equals(mCurrentUrl)) {
ivPic.setImageBitmap(result);
System.out.println("從網路獲取圖片");
// 從網路載入完之後,將圖片保存到本地SD卡一份,保存到內存中一份
localCacheUtils.setBitmap2Local(url, result);
// 從網路載入完之後,將圖片保存到本地SD卡一份,保存到內存中一份
memoryCacheUtils.setBitmap2Memory(url, result);
}
}
}
/**
* 下載網路圖片
*
* @param url
* @return
*/
private Bitmap downloadBitmap(String url) {
HttpURLConnection conn = null;
try {
URL mURL = new URL(url);
// 打開HttpURLConnection連接
conn = (HttpURLConnection) mURL.openConnection();
// 設置參數
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
// 開啟連接
conn.connect();
// 獲得響應碼
int code = conn.getResponseCode();
if (code == 200) {
// 相應成功,獲得網路返回來的輸入流
InputStream is = conn.getInputStream();
// 圖片的輸入流獲取成功之後,設置圖片的壓縮參數,將圖片進行壓縮
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;// 將圖片的寬高都壓縮為原來的一半,在開發中此參數需要根據圖片展示的大小來確定,否則可能展示的不正常
options.inPreferredConfig = Bitmap.Config.RGB_565;// 這個壓縮的最小
// Bitmap bitmap = BitmapFactory.decodeStream(is);
Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);// 經過壓縮的圖片
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 斷開連接
conn.disconnect();
}
return null;
}
}
Ⅲ 求解答,什麼是緩存,緩存的作用是什麼
緩存,電腦CPU是會用到,對CPU很重要。內存一般都有R0M和RAM倆個是不一樣的,ROM也就是電腦的硬碟,RAM是電腦的內存條。不過你說:當載入圖片過多的時候會很卡,有很多種原因,沒看現象我也不知道是什麼原因造成的。
Ⅳ bitmaputils緩存路徑在哪
答:loadAnimation()方法是AnimationUtils類的靜態方法。可以通過「類名.方法名()」的方式調用,需要new一個對象出來的再調用的是實例方法。 靜態方法是使用公共內存空間的,就是說所有對象都可以直接引用,不需要創建對象再使用該方法。實例方法是使...
Ⅳ 當一次應載入很多bitmap,怎麼使用緩存
在這些控制項中,當一個子控制項不顯示的時候,系統會重用該控制項來循環顯示 以便減少對內存的消耗。同時垃圾回收機制還會釋放那些已經載入內存中的Bitmap資源(假設您沒有強引用這些Bitmap)。一般來說這樣都是不錯的,但是在用戶來回滑動屏幕的時候,為了保證UI的流暢性和載入圖片的效率,您需要避免重復的處理這些需要顯示的圖片。 使用內存緩存和磁碟緩存可以解決這個問題,使用緩存可以讓控制項快速的載入已經處理過的圖片。
本文介紹如何使用緩存來提高UI的載入輸入和滑動的流暢性。
使用內存緩存
內存緩存提高了訪問圖片的速度,但是要佔用不少內存。 LruCache
類(在API 4之前可以使用Support Library 中的類 )特別適合緩存Bitmap, 把最近使用到的
Bitmap對象用強引用保存起來(保存到LinkedHashMap中),當緩存數量達到預定的值的時候,把
不經常使用的對象刪除。
注意: 過去,實現內存緩存的常用做法是使用
SoftReference 或者
WeakReference bitmap 緩存,
但是不推薦使用這種方式。從Android 2.3 (API Level 9) 開始,垃圾回收開始強制的回收掉 soft/weak 引用 從而導致這些緩存沒有任何效率的提升。
另外,在 Android 3.0 (API Level 11)之前,這些緩存的Bitmap數據保存在底層內存(native memory)中,並且達到預定條件後也不會釋放這些對象,從而可能導致
程序超過內存限制並崩潰。
在使用 LruCache 的時候,需要考慮如下一些因素來選擇一個合適的緩存數量參數:
1.程序中還有多少內存可用
2.同時在屏幕上顯示多少圖片?要先緩存多少圖片用來顯示到即將看到的屏幕上?
3.設備的屏幕尺寸和屏幕密度是多少?超高的屏幕密度(xhdpi 例如 Galaxy Nexus)
4.設備顯示同樣的圖片要比低屏幕密度(hdpi 例如 Nexus S)設備需要更多的內存。
5.圖片的尺寸和格式決定了每個圖片需要佔用多少內存
6.圖片訪問的頻率如何?一些圖片的訪問頻率要比其他圖片高很多?如果是這樣的話,您可能需要把這些經常訪問的圖片放到內存中。
7.在質量和數量上如何平衡?有些情況下保存大量的低質量的圖片是非常有用的,當需要的情況下使用後台線程來加入一個高質量版本的圖片。
這里沒有萬能配方可以適合所有的程序,您需要分析您的使用情況並在指定自己的緩存策略。使用太小的緩存並不能起到應有的效果,而使用太大的緩存會消耗更多
的內存從而有可能導致 java.lang.OutOfMemory 異常或者留下很少的內存供您的程序其他功能使用。
Ⅵ 在Android開發中,有哪些好的內存優化方式
1. 使用更加輕量的數據結構
例如,我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統數據結構。通常的HashMap的實現方式更加消耗內存,因為它需要一個額外的實例對象來記錄Mapping操作。另外,SparseArray更加高效,在於他們避免了對key與value的自動裝箱(autoboxing),並且避免了裝箱後的解箱。
2. 避免在Android裡面使用Enum
Android官方培訓課程提到過「Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.」,具體原理請參考《Android性能優化典範(三)》,所以請避免在Android裡面使用到枚舉。
3. 減小Bitmap對象的內存佔用
Bitmap是一個極容易消耗內存的大胖子,減小創建出來的Bitmap的內存佔用可謂是重中之重,,通常來說有以下2個措施:
inSampleSize:縮放比例,在把圖片載入內存之前,我們需要先計算出一個合適的縮放比例,避免不必要的大圖載入。
decode format:解碼格式,選擇ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差異
4.Bitmap對象的復用
縮小Bitmap的同時,也需要提高BitMap對象的復用率,避免頻繁創建BitMap對象,復用的方法有以下2個措施
LRUCache : 「最近最少使用演算法」在Android中有極其普遍的應用。ListView與GridView等顯示大量圖片的控制項里,就是使用LRU的機制來緩存處理好的Bitmap,把近期最少使用的數據從緩存中移除,保留使用最頻繁的數據,
inBitMap高級特性:利用inBitmap的高級特性提高Android系統在Bitmap分配與釋放執行效率。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的Bitmap會嘗試去使用之前那張Bitmap在Heap中所佔據的pixel data內存區域,而不是去問內存重新申請一塊區域來存放Bitmap。利用這種特性,即使是上千張的圖片,也只會僅僅只需要佔用屏幕所能夠顯示的圖片數量的內存大小
4. 使用更小的圖片
在涉及給到資源圖片時,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片。盡量使用更小的圖片不僅可以減少內存的使用,還能避免出現大量的InflationException。假設有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖時會因為內存不足而發生InflationException,這個問題的根本原因其實是發生了OOM。
5.StringBuilder
在有些時候,代碼中會需要使用到大量的字元串拼接的操作,這種時候有必要考慮使用StringBuilder來替代頻繁的「+」。
6.避免在onDraw方法裡面執行對象的創建
類似onDraw等頻繁調用的方法,一定需要注意避免在這里做創建對象的操作,因為他會迅速增加內存的使用,而且很容易引起頻繁的gc,甚至是內存抖動。
7. 避免對象的內存泄露
類的靜態變數持有大數據對象
靜態變數長期維持到大數據對象的引用,阻止垃圾回收。
非靜態內部類存在靜態實例
非靜態內部類會維持一個到外部類實例的引用,如果非靜態內部類的實例是靜態的,就會間接長期維持著外部類的引用,阻止被回收掉。
資源對象未關閉
資源性對象比如(Cursor,File文件等)往往都用了一些緩沖,我們在不使用的時候,應該及時關閉它們, 以便它們的緩沖及時回收內存。它們的緩沖不僅存在於java虛擬機內,還存在於java虛擬機外。 如果我們僅僅是把它的引用設置為null,而不關閉它們,往往會造成內存泄露。
解決辦法: 比如SQLiteCursor(在析構函數finalize(),如果我們沒有關閉它,它自己會調close()關閉), 如果我們沒有關閉它,系統在回收它時也會關閉它,但是這樣的效率太低了。 因此對於資源性對象在不使用的時候,應該調用它的close()函數,將其關閉掉,然後才置為null. 在我們的程序退出時一定要確保我們的資源性對象已經關閉。 程序中經常會進行查詢資料庫的操作,但是經常會有使用完畢Cursor後沒有關閉的情況。如果我們的查詢結果集比較小, 對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以後的測試和問題排查帶來困難和風險,記得try catch後,在finally方法中關閉連接
Handler內存泄漏
Handler作為內部類存在於Activity中,但是Handler生命周期與Activity生命周期往往並不是相同的,比如當Handler對象有Message在排隊,則無法釋放,進而導致本該釋放的Acitivity也沒有辦法進行回收。
Ⅶ android怎麼壓縮一個bitmap佔用空間大小
在Android應用里,最耗費內存的就是圖片資源。而且在Android系統中,讀取點陣圖Bitmap時,分給虛擬機中的圖片的堆棧大小隻有8M,如果超出了,就會出現OutOfMemory異常。所以,對於圖片的內存優化,是Android應用開發中比較重要的內容。 1) 要及時回收Bitmap的內存 Bitmap類有一個方法recycle(),從方法名可以看出意思是回收。這里就有疑問了,Android系統有自己的垃圾回收機制,可以不定期的回收掉不使用的內存空間,當然也包括Bitmap的空間。那為什麼還需要這個方法呢? Bitmap類的構造方法都是私有的,所以開發者不能直接new出一個Bitmap對象,只能通過BitmapFactory類的各種靜態方法來實例化一個Bitmap。仔細查看BitmapFactory的源代碼可以看到,生成Bitmap對象最終都是通過JNI調用方式實現的。所以,載入Bitmap到內存里以後,是包含兩部分內存區域的。簡單的說,一部分是Java部分的,一部分是C部分的。這個Bitmap對象是由Java部分分配的,不用的時候系統就會自動回收了,但是那個對應的C可用的內存區域,虛擬機是不能直接回收的,這個只能調用底層的功能釋放。所以需要調用recycle()方法來釋放C部分的內存。從Bitmap類的源代碼也可以看到,recycle()方法里也的確是調用了JNI方法了的。 那如果不調用recycle(),是否就一定存在內存泄露呢?也不是的。Android的每個應用都運行在獨立的進程里,有著獨立的內存,如果整個進程被應用本身或者系統殺死了,內存也就都被釋放掉了,當然也包括C部分的內存。 Android對於進程的管理是非常復雜的。簡單的說,Android系統的進程分為幾個級別,系統會在內存不足的情況下殺死一些低優先順序的進程,以提供給其它進程充足的內存空間。在實際項目開發過程中,有的開發者會在退出程序的時候使用Process.killProcess(Process.myPid())的方式將自己的進程殺死,但是有的應用僅僅會使用調用Activity.finish()方法的方式關閉掉所有的Activity。 經驗分享: Android手機的用戶,根據習慣不同,可能會有兩種方式退出整個應用程序:一種是按Home鍵直接退到桌面;另一種是從應用程序的退出按鈕或者按Back鍵退出程序。那麼從系統的角度來說,這兩種方式有什麼區別呢?按Home鍵,應用程序並沒有被關閉,而是成為了後台應用程序。按Back鍵,一般來說,應用程序關閉了,但是進程並沒有被殺死,而是成為了空進程(程序本身對退出做了特殊處理的不考慮在內)。 Android系統已經做了大量進程管理的工作,這些已經可以滿足用戶的需求。個人建議,應用程序在退出應用的時候不需要手動殺死自己所在的進程。對於應用程序本身的進程管理,交給Android系統來處理就可以了。應用程序需要做的,是盡量做好程序本身的內存管理工作。 一般來說,如果能夠獲得Bitmap對象的引用,就需要及時的調用Bitmap的recycle()方法來釋放Bitmap佔用的內存空間,而不要等Android系統來進行釋放。 下面是釋放Bitmap的示例代碼片段。 // 先判斷是否已經回收 if(bitmap != null && !bitmap.isRecycled()){ // 回收並且置為null bitmap.recycle(); bitmap = null; } System.gc(); 從上面的代碼可以看到,bitmap.recycle()方法用於回收該Bitmap所佔用的內存,接著將bitmap置空,最後使用System.gc()調用一下系統的垃圾回收器進行回收,可以通知垃圾回收器盡快進行回收。這里需要注意的是,調用System.gc()並不能保證立即開始進行回收過程,而只是為了加快回收的到來。 如何調用recycle()方法進行回收已經了解了,那什麼時候釋放Bitmap的內存比較合適呢?一般來說,如果代碼已經不再需要使用Bitmap對象了,就可以釋放了。釋放內存以後,就不能再使用該Bitmap對象了,如果再次使用,就會拋出異常。所以一定要保證不再使用的時候釋放。比如,如果是在某個Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中進行回收。 2) 捕獲異常 因為Bitmap是吃內存大戶,為了避免應用在分配Bitmap內存的時候出現OutOfMemory異常以後Crash掉,需要特別注意實例化Bitmap部分的代碼。通常,在實例化Bitmap的代碼中,一定要對OutOfMemory異常進行捕獲。 以下是代碼示例。 Bitmap bitmap = null; try { // 實例化Bitmap bitmap = BitmapFactory.decodeFile(path); } catch (OutOfMemoryError e) { // } if (bitmap == null) { // 如果實例化失敗 返回默認的Bitmap對象 return defaultBitmapMap; } 這里對初始化Bitmap對象過程中可能發生的OutOfMemory異常進行了捕獲。如果發生了OutOfMemory異常,應用不會崩潰,而是得到了一個默認的Bitmap圖。 經驗分享: 很多開發者會習慣性的在代碼中直接捕獲Exception。但是對於OutOfMemoryError來說,這樣做是捕獲不到的。因為OutOfMemoryError是一種Error,而不是Exception。在此僅僅做一下提醒,避免寫錯代碼而捕獲不到OutOfMemoryError。 3) 緩存通用的Bitmap對象 有時候,可能需要在一個Activity里多次用到同一張圖片。比如一個Activity會展示一些用戶的頭像列表,而如果用戶沒有設置頭像的話,則會顯示一個默認頭像,而這個頭像是位於應用程序本身的資源文件中的。 如果有類似上面的場景,就可以對同一Bitmap進行緩存。如果不進行緩存,盡管看到的是同一張圖片文件,但是使用BitmapFactory類的方法來實例化出來的Bitmap,是不同的Bitmap對象。緩存可以避免新建多個Bitmap對象,避免內存的浪費。 經驗分享: Web開發者對於緩存技術是很熟悉的。其實在Android應用開發過程中,也會經常使用緩存的技術。這里所說的緩存有兩個級別,一個是硬碟緩存,一個是內存緩存。比如說,在開發網路應用過程中,可以將一些從網路上獲取的數據保存到SD卡中,下次直接從SD卡讀取,而不從網路中讀取,從而節省網路流量。這種方式就是硬碟緩存。再比如,應用程序經常會使用同一對象,也可以放到內存中緩存起來,需要的時候直接從內存中讀取。這種方式就是內存緩存。 4) 壓縮圖片 如果圖片像素過大,使用BitmapFactory類的方法實例化Bitmap的過程中,需要大於8M的內存空間,就必定會發生OutOfMemory異常。這個時候該如何處理呢?如果有這種情況,則可以將圖片縮小,以減少載入圖片過程中的內存的使用,避免異常發生。 使用BitmapFactory.Options設置inSampleSize就可以縮小圖片。屬性值inSampleSize表示縮略圖大小為原始圖片大小的幾分之一。即如果這個值為2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片的大小就為原始大小的1/4。 如果知道圖片的像素過大,就可以對其進行縮小。那麼如何才知道圖片過大呢? 使用BitmapFactory.Options設置inJustDecodeBounds為true後,再使用decodeFile()等方法,並不會真正的分配空間,即解碼出來的Bitmap為null,但是可計算出原始圖片的寬度和高度,即options.outWidth和options.outHeight。通過這兩個值,就可以知道圖片是否過大了。 BitmapFactory.Options opts = new BitmapFactory.Options(); // 設置inJustDecodeBounds為true opts.inJustDecodeBounds = true; // 使用decodeFile方法得到圖片的寬和高 BitmapFactory.decodeFile(path, opts); // 列印出圖片的寬和高 Log.d("example", opts.outWidth + "," + opts.outHeight); 在實際項目中,可以利用上面的代碼,先獲取圖片真實的寬度和高度,然後判斷是否需要跑縮小。如果不需要縮小,設置inSampleSize的值為1。如果需要縮小,則動態計算並設置inSampleSize的值,對圖片進行縮小。需要注意的是,在下次使用BitmapFactory的decodeFile()等方法實例化Bitmap對象前,別忘記將opts.inJustDecodeBound設置回false。否則獲取的bitmap對象還是null。 經驗分享: 如果程序的圖片的來源都是程序包中的資源,或者是自己伺服器上的圖片,圖片的大小是開發者可以調整的,那麼一般來說,就只需要注意使用的圖片不要過大,並且注意代碼的質量,及時回收Bitmap對象,就能避免OutOfMemory異常的發生。 如果程序的圖片來自外界,這個時候就特別需要注意OutOfMemory的發生。一個是如果載入的圖片比較大,就需要先縮小;另一個是一定要捕獲異常,避免程序Crash。
Ⅷ 如何高效使用和管理Bitmap
一、圖片載入流程
首先,我們談談載入圖片的流程,項目中的該模塊處理流程如下:
1.在UI主線程中,從內存緩存中獲取圖片,找到後返回。找不到進入下一步;
2.在工作線程中,從磁碟緩存中獲取圖片,找到即返回並更新內存緩存。找不到進入下一步;
3.在工作線程中,從網路中獲取圖片,找到即返回並同時更新內存緩存和磁碟緩存。找不到顯示默認以提示。
二、內存緩存類(PanoMemCache)
這里使用Android提供的LruCache類,該類保存一個強引用來限制內容數量,每當Item被訪問的時候,此Item就會移動到隊列的頭部。當cache已滿的時候加入新的item時,在隊列尾部的item會被回收。
[java] view plain print ?
public class PanoMemoryCache {
// LinkedHashMap初始容量
private static final int INITIAL_CAPACITY = 16;
// LinkedHashMap載入因子
private static final int LOAD_FACTOR = 0.75f;
// LinkedHashMap排序模式
private static final boolean ACCESS_ORDER = true;
// 軟引用緩存
private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;
// 硬引用緩存
private static LruCache<String, Bitmap> mLruCache;
public PanoMemoryCache() {
// 獲取單個進程可用內存的最大值
// 方式一:使用ActivityManager服務(計量單位為M)
/*int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();*/
// 方式二:使用Runtime類(計量單位為Byte)
final int memClass = (int) Runtime.getRuntime().maxMemory();
// 設置為可用內存的1/4(按Byte計算)
final int cacheSize = memClass / 4;
mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
if(value != null) {
// 計算存儲bitmap所佔用的位元組數
return value.getRowBytes() * value.getHeight();
} else {
return 0;
}
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if(oldValue != null) {
// 當硬引用緩存容量已滿時,會使用LRU演算法將最近沒有被使用的圖片轉入軟引用緩存
mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
}
}
};
/*
* 第一個參數:初始容量(默認16)
* 第二個參數:載入因子(默認0.75)
* 第三個參數:排序模式(true:按訪問次數排序;false:按插入順序排序)
*/
mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(INITIAL_CAPACITY, LOAD_FACTOR, ACCESS_ORDER) {
private static final long serialVersionUID = 7237325113220820312L;
@Override
protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
if(size() > SOFT_CACHE_SIZE) {
return true;
}
return false;
}
};
}
/**
* 從緩存中獲取Bitmap
* @param url
* @return bitmap
*/
public Bitmap getBitmapFromMem(String url) {
Bitmap bitmap = null;
// 先從硬引用緩存中獲取
synchronized (mLruCache) {
bitmap = mLruCache.get(url);
if(bitmap != null) {
// 找到該Bitmap之後,將其移到LinkedHashMap的最前面,保證它在LRU演算法中將被最後刪除。
mLruCache.remove(url);
mLruCache.put(url, bitmap);
return bitmap;
}
}
// 再從軟引用緩存中獲取
synchronized (mSoftCache) {
SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
if(bitmapReference != null) {
bitmap = bitmapReference.get();
if(bitmap != null) {
// 找到該Bitmap之後,將它移到硬引用緩存。並從軟引用緩存中刪除。
mLruCache.put(url, bitmap);
mSoftCache.remove(url);
return bitmap;
} else {
mSoftCache.remove(url);
}
}
}
return null;
}
/**
* 添加Bitmap到內存緩存
* @param url
* @param bitmap
*/
public void addBitmapToCache(String url, Bitmap bitmap) {
if(bitmap != null) {
synchronized (mLruCache) {
mLruCache.put(url, bitmap);
}
}
}
/**
* 清理軟引用緩存
*/
public void clearCache() {
mSoftCache.clear();
mSoftCache = null;
}
}
補充一點,由於4.0平台以後對SoftReference類引用的對象調整了回收策略,所以該類中的軟引用緩存實際上沒什麼效果,可以去掉。2.3以前平台建議保留。
三、磁碟緩存類(PanoDiskCache)
五、使用decodeByteArray()還是decodeStream()?
講到這里,有童鞋可能會問我為什麼使用BitmapFactory.decodeByteArray(data, 0, data.length, opts)來創建Bitmap,而非使用BitmapFactory.decodeStream(is, null, opts)。你這樣做不是要多寫一個靜態方法readInputStream()嗎?
沒錯,decodeStream()確實是該使用情景下的首選方法,但是在有些情形下,它會導致圖片資源不能即時獲取,或者說圖片被它偷偷地緩存起來,交 還給我們的時間有點長。但是延遲性是致命的,我們等不起。所以在這里選用decodeByteArray()獲取,它直接從位元組數組中獲取,貼近於底層 IO、脫離平台限制、使用起來風險更小。
六、引入緩存機制後獲取圖片的方法
[java] view plain print ?
/**
* 載入Bitmap
* @param url
* @return
*/
private Bitmap loadBitmap(String url) {
// 從內存緩存中獲取,推薦在主UI線程中進行
Bitmap bitmap = memCache.getBitmapFromMem(url);
if(bitmap == null) {
// 從文件緩存中獲取,推薦在工作線程中進行
bitmap = diskCache.getBitmapFromDisk(url);
if(bitmap == null) {
// 從網路上獲取,不用推薦了吧,地球人都知道~_~
bitmap = PanoUtils.downloadBitmap(this, url);
if(bitmap != null) {
diskCache.addBitmapToCache(bitmap, url);
memCache.addBitmapToCache(url, bitmap);
}
} else {
memCache.addBitmapToCache(url, bitmap);
}
}
return bitmap;
}
七、工作線程池化
有關多線程的切換問題以及在UI線程中執行loadBitmap()方法無效的問題,請參見另一篇博文: 使用嚴苛模式打破Android4.0以上平台應用中UI主線程的「獨斷專行」。
有關工作線程的處理方式,這里推薦使用定製線程池的方式,核心代碼如下:
[java] view plain print ?
// 線程池初始容量
private static final int POOL_SIZE = 4;
private ExecutorService executorService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 獲取當前使用設備的CPU個數
int cpuNums = Runtime.getRuntime().availableProcessors();
// 預開啟線程池數目
executorService = Executors.newFixedThreadPool(cpuNums * POOL_SIZE);
...
executorService.submit(new Runnable() {
// 此處執行一些耗時工作,不要涉及UI工作。如果遇到,直接轉交UI主線程
pano.setImage(loadBitmap(url));
});
...
}
我們知道,線程構造也是比較耗資源的。一定要對其進行有效的管理和維護。千萬不要隨意而行,一張圖片的工作線程不搭理也許沒什麼,當使用場景變為 ListView和GridView時,線程池化工作就顯得尤為重要了。Android不是提供了AsyncTask嗎?為什麼不用它?其實 AsyncTask底層也是靠線程池支持的,它默認分配的線程數是128,是遠大於我們定製的executorService。