注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接:http://developer.android.com/training/displaying-bitmaps/manage-memory.html


接着上一节课的步伐,还有很多特定的事情可以让垃圾回收和位图重用变得容易。根据你的目标Android系统的不同版本,推荐的策略也会有所不同。这系列课程的样例代码:BitmapFun(链接:http://www.cnblogs.com/jdneo/p/3512517.html)向你展示了你的应用如何在不同的Android版本间有效率地工作。

在开始这堂课之前,我们先来看一下Android对于位图存储管理的演变:

  • 在Android 2.2(API Level 8)之前,当垃圾回收发生时,你应用的线程会停止。由此导致的延迟会降低应用的性能表现。在Android 2.3开始加入了并发式的垃圾回收,这意味着当一个位图不再被引用时,对应的内存会被迅速回收
  • 在Android 2.3.3(API Level 10)之前,一副位图的依托像素数据(backing pixel data)存储于本机内存中。它和位图本身是分离开的,位图存储于Dalvik堆中。本机内存中的像素数据并不会以一种可预测的形式进行释放,因此可能会导致一个应用超过它的内存限制而崩溃。从Android 3.0(API Level 11)开始,像素数据和与其关联的位图都存储于Dalvik堆中了

下面的章节将会描述如何对不同的Android版本,优化位图存储管理。


一). 在Android 2.3.3及之前的系统版本上管理内存

在Android 2.3.3(API Level 10)及以前,推荐使用recycle()。如果你在你的应用中要显示大量的位图数据,你极有可能引起OutOfMemoryError错误。recycle()可以让应用尽快地释放内存。

Caution:

只有当你确定对应的位图将不再被使用的情况下,你才应该使用recycle()。如果你使用了recycle(),并在之后尝试绘制该位图,那么你将会得到这样的错误提示:“Canvas: trying to use a recycled bitmap.”

下面的代码是调用recycle()的样例。它使用引用计数(变量“mDisplayRefCount”以及“mCacheRefCount”)来追踪位图是否是当前正在显示或是在缓存中。当下列情况发生时,代码会回收位图:

  • “mDisplayRefCount”以及“mCacheRefCount”的引用计数都为0。
  • 位图不是“null”,同时它还未被回收。
  1. private int mCacheRefCount = 0;
  2. private int mDisplayRefCount = 0;
  3. ...
  4. // Notify the drawable that the displayed state has changed.
  5. // Keep a count to determine when the drawable is no longer displayed.
  6. public void setIsDisplayed(boolean isDisplayed) {
  7. synchronized (this) {
  8. if (isDisplayed) {
  9. mDisplayRefCount++;
  10. mHasBeenDisplayed = true;
  11. } else {
  12. mDisplayRefCount--;
  13. }
  14. }
  15. // Check to see if recycle() can be called.
  16. checkState();
  17. }
  18.  
  19. // Notify the drawable that the cache state has changed.
  20. // Keep a count to determine when the drawable is no longer being cached.
  21. public void setIsCached(boolean isCached) {
  22. synchronized (this) {
  23. if (isCached) {
  24. mCacheRefCount++;
  25. } else {
  26. mCacheRefCount--;
  27. }
  28. }
  29. // Check to see if recycle() can be called.
  30. checkState();
  31. }
  32.  
  33. private synchronized void checkState() {
  34. // If the drawable cache and display ref counts = 0, and this drawable
  35. // has been displayed, then recycle.
  36. if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
  37. && hasValidBitmap()) {
  38. getBitmap().recycle();
  39. }
  40. }
  41.  
  42. private synchronized boolean hasValidBitmap() {
  43. Bitmap bitmap = getBitmap();
  44. return bitmap != null && !bitmap.isRecycled();
  45. }

二). 在Android 3.0及以后的系统版本上管理内存

Android 3.0(API Level 11)引入了BitmapFactory.Options.inBitmap域。如果配置了该选项,接受该Options对象的解码方法会在加载内容时尝试去重用已经存在的位图。这意味着位图内存被重用了,从而提高了性能表现,同时免去了内存分配和回收的工作。然而,对于如何使用inBitmap会有一些限制。特别地,在Android 4.4(API Level 19)之前,只有尺寸吻合的位图才被支持。更多详细信息,可以阅读:inBitmap

保存一幅位图以备将来使用

下面的代码样例展示了在相同的应用中,一个已经存在的位图是如何为了将来可能被再次使用到而保存的。当一个应用在Android 3.0及以上的设备上运行,且一个位图从LruCache中移除,该位图的软引用会放置在一个HashSet中,以备之后inBitmap使用:

  1. Set<SoftReference<Bitmap>> mReusableBitmaps;
  2. private LruCache<String, BitmapDrawable> mMemoryCache;
  3.  
  4. // If you're running on Honeycomb or newer, create a
  5. // synchronized HashSet of references to reusable bitmaps.
  6. if (Utils.hasHoneycomb()) {
  7. mReusableBitmaps =
  8. Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
  9. }
  10.  
  11. mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {
  12.  
  13. // Notify the removed entry that is no longer being cached.
  14. @Override
  15. protected void entryRemoved(boolean evicted, String key,
  16. BitmapDrawable oldValue, BitmapDrawable newValue) {
  17. if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
  18. // The removed entry is a recycling drawable, so notify it
  19. // that it has been removed from the memory cache.
  20. ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
  21. } else {
  22. // The removed entry is a standard BitmapDrawable.
  23. if (Utils.hasHoneycomb()) {
  24. // We're running on Honeycomb or later, so add the bitmap
  25. // to a SoftReference set for possible use with inBitmap later.
  26. mReusableBitmaps.add
  27. (new SoftReference<Bitmap>(oldValue.getBitmap()));
  28. }
  29. }
  30. }
  31. ....
  32. }

使用一个存在的位图

在运行的程序中,解码方法检查是否有可用的位图。例如:

  1. public static Bitmap decodeSampledBitmapFromFile(String filename,
  2. int reqWidth, int reqHeight, ImageCache cache) {
  3.  
  4. final BitmapFactory.Options options = new BitmapFactory.Options();
  5. ...
  6. BitmapFactory.decodeFile(filename, options);
  7. ...
  8.  
  9. // If we're running on Honeycomb or newer, try to use inBitmap.
  10. if (Utils.hasHoneycomb()) {
  11. addInBitmapOptions(options, cache);
  12. }
  13. ...
  14. return BitmapFactory.decodeFile(filename, options);
  15. }

下面的代码是上述代码中addInBitmapOptions()方法的实现。它寻找一个存在的位图,并将它作为inBitmap的值。注意,该方法仅仅在它找到了一个合适的匹配时,才将位图设置为inBitmap的值(你的代码不可以假定这个匹配一定能找到):

  1. private static void addInBitmapOptions(BitmapFactory.Options options,
  2. ImageCache cache) {
  3. // inBitmap only works with mutable bitmaps, so force the decoder to
  4. // return mutable bitmaps.
  5. options.inMutable = true;
  6.  
  7. if (cache != null) {
  8. // Try to find a bitmap to use for inBitmap.
  9. Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
  10.  
  11. if (inBitmap != null) {
  12. // If a suitable bitmap has been found, set it as the value of
  13. // inBitmap.
  14. options.inBitmap = inBitmap;
  15. }
  16. }
  17. }
  18.  
  19. // This method iterates through the reusable bitmaps, looking for one
  20. // to use for inBitmap:
  21. protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
  22. Bitmap bitmap = null;
  23.  
  24. if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
  25. synchronized (mReusableBitmaps) {
  26. final Iterator<SoftReference<Bitmap>> iterator
  27. = mReusableBitmaps.iterator();
  28. Bitmap item;
  29.  
  30. while (iterator.hasNext()) {
  31. item = iterator.next().get();
  32.  
  33. if (null != item && item.isMutable()) {
  34. // Check to see it the item can be used for inBitmap.
  35. if (canUseForInBitmap(item, options)) {
  36. bitmap = item;
  37.  
  38. // Remove from reusable set so it can't be used again.
  39. iterator.remove();
  40. break;
  41. }
  42. } else {
  43. // Remove from the set if the reference has been cleared.
  44. iterator.remove();
  45. }
  46. }
  47. }
  48. }
  49. return bitmap;
  50. }

最后,此方法决定备选的位图是否符合inBitmap的尺寸标准:

  1. static boolean canUseForInBitmap(
  2. Bitmap candidate, BitmapFactory.Options targetOptions) {
  3.  
  4. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  5. // From Android 4.4 (KitKat) onward we can re-use if the byte size of
  6. // the new bitmap is smaller than the reusable bitmap candidate
  7. // allocation byte count.
  8. int width = targetOptions.outWidth / targetOptions.inSampleSize;
  9. int height = targetOptions.outHeight / targetOptions.inSampleSize;
  10. int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
  11. return byteCount <= candidate.getAllocationByteCount();
  12. }
  13.  
  14. // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
  15. return candidate.getWidth() == targetOptions.outWidth
  16. && candidate.getHeight() == targetOptions.outHeight
  17. && targetOptions.inSampleSize == 1;
  18. }
  19.  
  20. /**
  21. * A helper function to return the byte usage per pixel of a bitmap based on its configuration.
  22. */
  23. static int getBytesPerPixel(Config config) {
  24. if (config == Config.ARGB_8888) {
  25. return 4;
  26. } else if (config == Config.RGB_565) {
  27. return 2;
  28. } else if (config == Config.ARGB_4444) {
  29. return 2;
  30. } else if (config == Config.ALPHA_8) {
  31. return 1;
  32. }
  33. return 1;
  34. }

【Android Developers Training】 59. 管理图片存储的更多相关文章

  1. 【Android Developers Training】 80. 管理网络使用

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  2. 【Android Developers Training】 43. 序言:管理音频播放

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  3. 【Android Developers Training】 14. 序言:管理Activity生命周期

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  4. 【Android Developers Training】 55. 序言:高效显示位图

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  5. 【Android Developers Training】 2. 运行你的应用

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  6. 【Android Developers Training】 108. 使用模拟定位进行测试

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. 【Android Developers Training】 94. 创建一个空内容提供器(Content Provider)

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 【Android Developers Training】 93. 创建一个空验证器

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  9. 【Android Developers Training】 92. 序言:使用同步适配器传输数据

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

随机推荐

  1. NancyFx 2.0的开源框架的使用-Stateless

    同样和前面一样新建一个空的Web项目,都在根目录添加Module,Models,Views文件夹 添加Nuget包 在Models文件夹里面添加UserModel类 public string Use ...

  2. 弹出输入内容prompt

    <script> window.onload = function(){ var oBtn = document.getElementById( "btn" ); oB ...

  3. VR全景是继互联网后的第二王朝吗?

    VR虚拟现实.VR全景广泛用于游戏中,带上VR眼镜,有身临其境般的感觉.于是近些年围绕着 "下一代计算平台",国内外兴起一股虚拟现实热,在这样的形势下,VR眼镜在国内打的十分火热. ...

  4. Not supported by Zabbix Agent & zabbix agent重装

    zabbix服务器显示一些监控项不起效,提示错误[Not supported by Zabbix Agent], 最后定位为zabbix客户端版本过低. Not supported by Zabbix ...

  5. kafka 0.8.2 消息生产者 producer

    package com.hashleaf.kafka; import java.util.Properties; import kafka.javaapi.producer.Producer; imp ...

  6. 地理位置 API

    js获取地理位置的接口navigator.geolocation geolocation对象有三个方法 1.getCurrentPosition 2.watchPosition 3.clearWatc ...

  7. 2017-5-31 VBA设置config sheet 制作工具

    最近学习了对单元格式进行设置的两种方式,一个是把一个sheet设置成config的配置,之后把内容读进去:一个是在sheet中读取XML文件. 今天先说说怎么用config来读取数据. 把这一个she ...

  8. 部署项目到weblogic时提示文件被锁,导致报错

    部署项目到weblogic中出现一个“黄叹号!”.报错如下: (1) Deployment is out of date due to changes in the underlying projec ...

  9. qrcode生成二维码插件

    今天我要和大家分享的是利用qrcode来生成二维码. 首先要使用qrcode就需要引用文件,我这边用的是1.7.2版本的jquery加上qrcode <script type="tex ...

  10. JavaSE教程-04Java中循环语句for,while,do···while-练习2

    1.编写一个剪子石头布对战小程序 该法是穷举法:将所有情况列出来 import java.util.*; public class Game{ public static void main(Stri ...