日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【Google官方教程】第三课:缓存Bitmap

發(fā)布時(shí)間:2025/4/5 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Google官方教程】第三课:缓存Bitmap 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

轉(zhuǎn)載聲明:Ryan的博客文章歡迎您的轉(zhuǎn)載,但在轉(zhuǎn)載的同時(shí),請(qǐng)注明文章的來(lái)源出處,不勝感激! :-)?

http://my.oschina.net/ryanhoo/blog/88443

譯者:Ryan Hoo

來(lái)源:https://developer.android.com/develop/index.html

譯者按:?在Google最新的文檔中,提供了一系列含金量相當(dāng)高的教程。因?yàn)榉N種原因而鮮為人知,真是可惜!Ryan將會(huì)細(xì)心整理,將之翻譯成中文,希望對(duì)開(kāi)發(fā)者有所幫助。

????? ? 本系列是Google關(guān)于展示大Bitmap(位圖)的官方演示,可以有效的解決內(nèi)存限制,更加有效的加載并顯示圖片,同時(shí)避免讓人頭疼的OOM(Out Of Memory)。

-------------------------------------------------------------------------------------

譯文:

????????加載一個(gè)Bitmap(位圖)到你的UI界面是非常簡(jiǎn)單的,但是如果你要一次加載一大批,事情就變得復(fù)雜多了。在大多數(shù)的情況下(如ListView、GridView或者ViewPager這樣的組件),屏幕上的圖片以及馬上要在滾動(dòng)到屏幕上顯示的圖片的總量,在本質(zhì)上是不受限制的。

????????像這樣的組件在子視圖移出屏幕后會(huì)進(jìn)行視圖回收,內(nèi)存使用仍被保留。但假設(shè)你不保留任何長(zhǎng)期存活的引用,垃圾回收器也會(huì)釋放你所加載的Bitmap。這自然再好不過(guò)了,但是為了保持流暢且快速加載的UI,你要避免繼續(xù)在圖片回到屏幕上的時(shí)候重新處理。使用內(nèi)存和硬盤(pán)緩存通常能解決這個(gè)問(wèn)題,使用緩存允許組件快速加載并處理圖片。

? ??????這節(jié)課將帶你使用內(nèi)存和硬盤(pán)緩存Bitmap,以在加載多個(gè)Bitmap的時(shí)候提升UI的響應(yīng)性和流暢性。

使用內(nèi)存緩存

????????以犧牲寶貴的應(yīng)用內(nèi)存為代價(jià),內(nèi)存緩存提供了快速的Bitmap訪(fǎng)問(wèn)方式。LruCache類(lèi)(可以在Support Library中獲取并支持到API ?Level 4以上,即1.6版本以上)是非常適合用作緩存Bitmap任務(wù)的,它將最近被引用到的對(duì)象存儲(chǔ)在一個(gè)強(qiáng)引用的LinkedHashMap中,并且在緩存超過(guò)了指定大小之后將最近不常使用的對(duì)象釋放掉。

? ? ? ??注意:以前有一個(gè)非常流行的內(nèi)存緩存實(shí)現(xiàn)是SoftReference(軟引用)或者WeakReference(弱引用)的Bitmap緩存方案,然而現(xiàn)在已經(jīng)不推薦使用了。自Android2.3版本(API Level 9)開(kāi)始,垃圾回收器更著重于對(duì)軟/弱引用的回收,這使得上述的方案相當(dāng)無(wú)效。此外,Android 3.0(API Level 11)之前的版本中,Bitmap的備份數(shù)據(jù)直接存儲(chǔ)在本地內(nèi)存中并以一種不可預(yù)測(cè)的方式從內(nèi)存中釋放,很可能短暫性的引起程序超出內(nèi)存限制而崩潰。

? ??????為了給LruCache選擇一個(gè)合適的大小,要考慮到很多原因,例如:

  • 其他的Activity(活動(dòng))和(或)程序都是很耗費(fèi)內(nèi)存的嗎?
  • 屏幕上一次會(huì)顯示多少圖片?有多少圖片將在屏幕上顯示?
  • 設(shè)備的屏幕大小和密度是多少?一個(gè)超高清屏幕(xhdpi)的設(shè)備如Galaxy Nexus,相比Nexus S(hdpi)來(lái)說(shuō),緩存同樣數(shù)量的圖片需要更大的緩存空間。
  • Bitmap的尺寸、配置以及每張圖片需要占用多少內(nèi)存?
  • 圖片的訪(fǎng)問(wèn)是否頻繁?有些會(huì)比其他的更加被頻繁的訪(fǎng)問(wèn)到嗎?如果是這樣,也許你需要將某些圖片一直保留在內(nèi)存中,甚至需要多個(gè)LruCache對(duì)象分配給不同組的Bitmap。
  • 你能平衡圖片的質(zhì)量和數(shù)量么?有的時(shí)候存儲(chǔ)大量低質(zhì)量的圖片更加有用,然后可以在后臺(tái)任務(wù)中加載另一個(gè)高質(zhì)量版本的圖片。

????????對(duì)于設(shè)置緩存大小,并沒(méi)有適用于所有應(yīng)用的規(guī)范,它取決于你在內(nèi)存使用分析后給出的合適的解決方案。緩存空間太小并無(wú)益處,反而會(huì)引起額外的開(kāi)銷(xiāo),而太大了又可能再次引起java.lang.OutOfMemory異常或只留下很小的空間給應(yīng)用的其他程序運(yùn)行。???

????????這里有一個(gè)設(shè)置Bitmap的LruCache示例:

private LruCache<String, Bitmap> mMemoryCache;@Override protected void onCreate(Bundle savedInstanceState) {...// Get memory class of this device, exceeding this amount will throw an// OutOfMemory exception.final int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();// Use 1/8th of the available memory for this memory cache.final int cacheSize = 1024 * 1024 * memClass / 8;mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {// The cache size will be measured in bytes rather than number of items.return bitmap.getByteCount();}};... }public void addBitmapToMemoryCache(String key, Bitmap bitmap) {if (getBitmapFromMemCache(key) == null) {mMemoryCache.put(key, bitmap);} }public Bitmap getBitmapFromMemCache(String key) {return mMemoryCache.get(key); }


??注意:在這個(gè)例子中,1/8的應(yīng)用內(nèi)存被分配給緩存。在一個(gè)普通的/hdpi設(shè)備上最低也在4M左右(32/8)。一個(gè)分辨率為800*480的設(shè)備上,全屏的填滿(mǎn)圖片的GridView占用的內(nèi)存約1.5M(800*480*4字節(jié)),因此這個(gè)大小的內(nèi)存可以緩存2.5頁(yè)左右的圖片。

????????當(dāng)加載一個(gè)Bitmap到ImageView中,先要檢查LruCache。如果有相應(yīng)的數(shù)據(jù),則立即用來(lái)更新ImageView,否則將啟動(dòng)后臺(tái)線(xiàn)程來(lái)處理這個(gè)圖片。

public void loadBitmap(int resId, ImageView imageView) {final String imageKey = String.valueOf(resId);final Bitmap bitmap = getBitmapFromMemCache(imageKey);if (bitmap != null) {mImageView.setImageBitmap(bitmap);} else {mImageView.setImageResource(R.drawable.image_placeholder);BitmapWorkerTask task = new BitmapWorkerTask(mImageView);task.execute(resId);} } ? BitmapWorkerTask 也需要更新內(nèi)存中的數(shù)據(jù): class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {...// Decode image in background.@Overrideprotected Bitmap doInBackground(Integer... params) {final Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), params[0], 100, 100));addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);return bitmap;}... }

使用硬盤(pán)緩存

????? ??一個(gè)內(nèi)存緩存對(duì)加速訪(fǎng)問(wèn)最近瀏覽過(guò)的Bitmap非常有幫助,但是你不能局限于內(nèi)存中的可用圖片。GridView這樣有著更大的數(shù)據(jù)集的組件可以很輕易消耗掉內(nèi)存緩存。你的應(yīng)用有可能在執(zhí)行其他任務(wù)(如打電話(huà))的時(shí)候被打斷,并且在后臺(tái)的任務(wù)有可能被殺死或者緩存被釋放。一旦用戶(hù)重新聚焦(resume)到你的應(yīng)用,你得再次處理每一張圖片。

????????在這種情況下,硬盤(pán)緩存可以用來(lái)存儲(chǔ)Bitmap并在圖片被內(nèi)存緩存釋放后減小圖片加載的時(shí)間(次數(shù))。當(dāng)然,從硬盤(pán)加載圖片比內(nèi)存要慢,并且應(yīng)該在后臺(tái)線(xiàn)程進(jìn)行,因?yàn)橛脖P(pán)讀取的時(shí)間是不可預(yù)知的。

????????注意:如果訪(fǎng)問(wèn)圖片的次數(shù)非常頻繁,那么ContentProvider可能更適合用來(lái)存儲(chǔ)緩存圖片,例如Image Gallery這樣的應(yīng)用程序。

????????這個(gè)類(lèi)中的示例代碼使用DiskLruCache(來(lái)自Android源碼)實(shí)現(xiàn)。在示例代碼中,除了已有的內(nèi)存緩存,還添加了硬盤(pán)緩存。

private DiskLruCache mDiskLruCache; private final Object mDiskCacheLock = new Object(); private boolean mDiskCacheStarting = true; private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB private static final String DISK_CACHE_SUBDIR = "thumbnails";@Override protected void onCreate(Bundle savedInstanceState) {...// Initialize memory cache...// Initialize disk cache on background threadFile cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);new InitDiskCacheTask().execute(cacheDir);... }class InitDiskCacheTask extends AsyncTask<File, Void, Void> {@Overrideprotected Void doInBackground(File... params) {synchronized (mDiskCacheLock) {File cacheDir = params[0];mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);mDiskCacheStarting = false; // Finished initializationmDiskCacheLock.notifyAll(); // Wake any waiting threads}return null;} }class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {...// Decode image in background.@Overrideprotected Bitmap doInBackground(Integer... params) {final String imageKey = String.valueOf(params[0]);// Check disk cache in background threadBitmap bitmap = getBitmapFromDiskCache(imageKey);if (bitmap == null) { // Not found in disk cache// Process as normalfinal Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), params[0], 100, 100));}// Add final bitmap to cachesaddBitmapToCache(imageKey, bitmap);return bitmap;}... }public void addBitmapToCache(String key, Bitmap bitmap) {// Add to memory cache as beforeif (getBitmapFromMemCache(key) == null) {mMemoryCache.put(key, bitmap);}// Also add to disk cachesynchronized (mDiskCacheLock) {if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {mDiskLruCache.put(key, bitmap);}} }public Bitmap getBitmapFromDiskCache(String key) {synchronized (mDiskCacheLock) {// Wait while disk cache is started from background threadwhile (mDiskCacheStarting) {try {mDiskCacheLock.wait();} catch (InterruptedException e) {}}if (mDiskLruCache != null) {return mDiskLruCache.get(key);}}return null; }// Creates a unique subdirectory of the designated app cache directory. Tries to use external // but if not mounted, falls back on internal storage. public static File getDiskCacheDir(Context context, String uniqueName) {// Check if media is mounted or storage is built-in, if so, try and use external cache dir// otherwise use internal cache dirfinal String cachePath =Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :context.getCacheDir().getPath();return new File(cachePath + File.separator + uniqueName); }

注意:即便是硬盤(pán)緩存初始化也需要硬盤(pán)操作,因此不應(yīng)該在主線(xiàn)程執(zhí)行。但是,這意味著硬盤(pán)緩存在初始化前就能被訪(fǎng)問(wèn)到。為了解決這個(gè)問(wèn)題,在上面的實(shí)現(xiàn)中添加了一個(gè)鎖對(duì)象(lock object),以確保在緩存被初始化之前應(yīng)用無(wú)法訪(fǎng)問(wèn)硬盤(pán)緩存。

????????在UI線(xiàn)程中檢查內(nèi)存緩存,相應(yīng)的硬盤(pán)緩存檢查應(yīng)在后臺(tái)線(xiàn)程中進(jìn)行。硬盤(pán)操作永遠(yuǎn)不要在UI線(xiàn)程中發(fā)生。當(dāng)圖片處理完成后,最終的Bitmap要被添加到內(nèi)存緩存和硬盤(pán)緩存中,以便后續(xù)的使用。

?處理配置更改

????????運(yùn)行時(shí)的配置會(huì)發(fā)生變化,例如屏幕方向的改變,會(huì)導(dǎo)致Android銷(xiāo)毀并以新的配置重新啟動(dòng)Activity(關(guān)于此問(wèn)題的更多信息,請(qǐng)參閱Handling Runtime Changes)。為了讓用戶(hù)有著流暢而快速的體驗(yàn),你需要在配置發(fā)生改變的時(shí)候避免再次處理所有的圖片。

????????幸運(yùn)的是,你在“使用內(nèi)存緩存”一節(jié)中為Bitmap構(gòu)造了很好的內(nèi)存緩存。這些內(nèi)存可以通過(guò)使用Fragment傳遞到信的Activity(活動(dòng))實(shí)例,這個(gè)Fragment可以調(diào)用setRetainInstance(true)方法保留下來(lái)。在Activity(活動(dòng))被重新創(chuàng)建后,你可以在上面的Fragment中訪(fǎng)問(wèn)到已經(jīng)存在的緩存對(duì)象,使得圖片能快加載并重新填充到ImageView對(duì)象中。

????????下面是一個(gè)使用FragmentLruCache對(duì)象保留在配置更改中的示例:

private LruCache<String, Bitmap> mMemoryCache;@Override protected void onCreate(Bundle savedInstanceState) {...RetainFragment mRetainFragment =RetainFragment.findOrCreateRetainFragment(getFragmentManager());mMemoryCache = RetainFragment.mRetainedCache;if (mMemoryCache == null) {mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {... // Initialize cache here as usual}mRetainFragment.mRetainedCache = mMemoryCache;}... }class RetainFragment extends Fragment {private static final String TAG = "RetainFragment";public LruCache<String, Bitmap> mRetainedCache;public RetainFragment() {}public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);if (fragment == null) {fragment = new RetainFragment();}return fragment;}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRetainInstance(true);} } ?為了測(cè)試這個(gè),可以在不適用Fragment的情況下旋轉(zhuǎn)設(shè)備屏幕。在保留緩存的情況下,你應(yīng)該能發(fā)現(xiàn)填充圖片到Activity中幾乎是瞬間從內(nèi)存中取出而沒(méi)有任何延遲的感覺(jué)。任何圖片優(yōu)先從內(nèi)存緩存獲取,沒(méi)有的話(huà)再到硬盤(pán)緩存中找,如果都沒(méi)有,那就以普通方式加載圖片。 ?

轉(zhuǎn)載于:https://my.oschina.net/u/559701/blog/88596

總結(jié)

以上是生活随笔為你收集整理的【Google官方教程】第三课:缓存Bitmap的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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