Bitmap内存问题

本文介绍如何在Android应用中优化Bitmap的使用,包括预缩放图片、调整Dalvik堆内存、及时释放Bitmap对象等策略,以避免内存溢出问题。
Bitmap内存问题

在Android应用里,最耗费内存的就是图片资源。而且在Android系统中,读取位图Bitmap时,分给虚拟机中的图片的堆栈大小只有8M,如果超出了,就会出现OutOfMemory异常。
 OOM 两个原因:(1)图片太大;(2)创建太多的bitmap对象而又没有及时释放。

释放一个bitmap对象标准方法
    if(!bmp.isRecycle() ){
              bmp.recycle()   //回收图片所占的内存
                 system.gc()  //提醒系统及时回收
      }
  
bmp=null;
解决方法:
《一》图片预先缩放
这种方法只是对图片做了一个缩放处理,降低了图片的分辨率,在需要保证图片质量的应用中不可取。
尽量不使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 
因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 
    因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间. 
    如果在读取时加上图片的Config参数,可以跟有效减少加载的内存,从而跟有效阻止抛out of Memory异常.另外,decodeStream直接拿的图片来读取字节码了,不会根据机器的各种分辨率来自动适应,使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源,否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了. 

       
  1.  //使用BitmapFactory.Options的inSampleSize参数来缩放  
  2.     public static Drawable resizeImage2(String path,  
  3.             int width,int height)   
  4.     {  
  5.         BitmapFactory.Options options = new BitmapFactory.Options();  
  6.         options.inJustDecodeBounds = true;//不加载bitmap到内存中  
  7.         BitmapFactory.decodeFile(path,options);   
  8.         int outWidth = options.outWidth;  
  9.         int outHeight = options.outHeight;  
  10.         options.inDither = false;  
  11.         options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
  12.         options.inSampleSize = 1;  
  13.           
  14.         if (outWidth != 0 && outHeight != 0 && width != 0 && height != 0)   
  15.         {  
  16.             int sampleSize=(outWidth/width+outHeight/height)/2;  
  17.             Log.d(tag, "sampleSize = " + sampleSize);  
  18.             options.inSampleSize = sampleSize;  
  19.         }  
  20.       
  21.         options.inJustDecodeBounds = false;  
  22.         return new BitmapDrawable(BitmapFactory.decodeFile(path, options));       
  23.     }  
  

《二》普通的图片缩放方法
这种方法代码相对简单,即先把源图加载到内存再处理,适合于源图已经在内存的情况。当从网络上直接读取图片的时候可以用这个方法缩放到指定的大小再显示。
  1. //使用Bitmap加Matrix来缩放  
  2.     public static Drawable resizeImage(Bitmap bitmap, int w, int h)   
  3.     {    
  4.         Bitmap BitmapOrg = bitmap;    
  5.         int width = BitmapOrg.getWidth();    
  6.         int height = BitmapOrg.getHeight();    
  7.         int newWidth = w;    
  8.         int newHeight = h;    
  9.   
  10.         float scaleWidth = ((float) newWidth) / width;    
  11.         float scaleHeight = ((float) newHeight) / height;    
  12.   
  13.         Matrix matrix = new Matrix();    
  14.         matrix.postScale(scaleWidth, scaleHeight);    
  15.         // if you want to rotate the Bitmap     
  16.         // matrix.postRotate(45);     
  17.         Bitmap resizedBitmap = Bitmap.createBitmap(BitmapOrg, 00, width,    
  18.                         height, matrix, true);    
  19.         return new BitmapDrawable(resizedBitmap);    
  20.     }  

《三》Dalvik虚拟机的堆内存分配
堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,当堆内存实际的利用率偏离设定的值的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。比如初始的HEAP是4M大小,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。
  1. int newSize = 4 * 1024 * 1024 ; //设置最小堆内存大小为4MB  
  2. VMRuntime.getRuntime().setMinimumHeapSize(newSize);  
  3. VMRuntime.getRuntime().setTargetHeapUtilization(0.75); // 设置堆内存的利用率为75%  

《四》Bitmap对象及时释放
Bitmap类的构造方法都是私有的,所以开发者不能直接new出一个Bitmap对象,只能通过BitmapFactory类的各种静态方法来实例化一个Bitmap。仔细查看BitmapFactory的源代码可以看到,生成Bitmap对象最终都是通过JNI调用方式实现的。所以,加载Bitmap到内存里以后,是包含两部分内存区域的。简单的说,一部分是Java部分的,一部分是C部分的。这个Bitmap对象是由Java部分分配的,不用的时候系统就会自动回收了,但是那个对应的C可用的内存区域,虚拟机是不能直接回收的,这个只能调用底层的功能释放。所以需要调用recycle()方法来释放C部分的内存。从Bitmap类的源代码也可以看到,recycle()方法里也的确是调用了JNI方法了的。

《五》创建图片缓冲
有时候,可能需要在一个Activity里多次用到同一张图片。比如一个Activity会展示一些用户的头像列表,而如果用户没有设置头像的话,则会显示一个默认头像,而这个头像是位于应用程序本身的资源文件中的。
如果有类似上面的场景,就可以对同一Bitmap进行缓存。如果不进行缓存,尽管看到的是同一张图片文件,但是使用BitmapFactory类的方法来实例化出来的Bitmap,是不同的Bitmap对象。缓存可以避免新建多个Bitmap对象,避免内存的浪费。

《六》优化Adapter
ListView,GridView,Gallery等可以使用缓存的View,我们可以只保留屏幕可见区域需要显示的Bitmap,而释放其他不需要显示的。
当ListView列表里的每一项显示时,都会调用Adapter的getView方法返回一个View,
来向ListView提供所需要的View对象。
下面是一个完整的getView()方法的代码示例。
public View getView(int position, View convertView, ViewGroup parent) {
  ViewHolder holder;
if (convertView == null) {
      convertView = mInflater.inflate(R.layout.list_item, null);
      holder = new ViewHolder();
      holder.text = (TextView) convertView.findViewById(R.id.text);
      convertView.setTag(holder);
  } else {
      holder = (ViewHolder) convertView.getTag();
  }
  holder.text.setText("line" + position);
  return convertView;
}
private class ViewHolder {
  TextView text;
}
当向上滚动ListView时,getView()方法会被反复调用。getView()的第二个参数convertView是被缓存起来的List条目中
的View对象。当ListView滑动的时候,getView可能会直接返回旧的convertView。这里使用了convertView和ViewHolder,可以充分利用缓存,避免反复创建View对象和TextView对象。如果ListView的条目只有几个,这种技巧并不能带来多少性能的提升。但是如果条目有几百甚至几千个,使用这种技巧只会创建几个convertView和ViewHolder(取决于当前界面能够显示的条目数),性能的差别就非常非常大了。

《七》使用软引用




<think>好的,我现在需要帮用户介绍一下安卓中的Bitmap内存分析。首先,我得回忆一下自己之前学过的相关知识,确保准确无误。Bitmap在安卓中确实是一个比较重要的部分,同时也是内存消耗的大户,所以分析它的内存使用情况对于优化应用性能很关键。 首先,我应该从Bitmap内存管理机制开始讲起。记得安卓的Bitmap内存分为两部分:Java层和Native层。在早期的版本中,比如Android 3.0之前,Bitmap的像素数据是存放在Java堆中的,但之后改到了Native堆,这样可以避免Java堆的内存限制,减少GC的影响。不过,从Android 8.0开始,又改回了Java堆,这可能是因为内存管理的优化或者其他原因。我需要确认一下这些版本变化的具体细节,避免给用户错误的信息。 接下来,用户可能需要知道如何计算Bitmap占用的内存。这里的关键是内存的计算公式:内存大小 = 图片宽度 × 图片高度 × 每个像素占用的字节数。每个像素的字节数取决于Bitmap的配置,比如ARGB_8888是4字节,RGB_565是2字节,ALPHA_8是1字节。需要注意的是,这里计算的是解码后的内存占用,和图片文件的大小不同,比如一张PNG图片文件可能只有100KB,但解码成Bitmap后可能占用几MB的内存。这点需要强调,因为很多开发者容易混淆这两个概念。 然后,用户可能想知道如何分析Bitmap内存使用情况。这时候需要介绍一些工具,比如Android Studio的Profiler,可以实时查看内存分配情况,还有Android的API,如Bitmap.getAllocationByteCount()来获取实际分配的内存。另外,命令行工具如adb shell dumpsys meminfo可以用来查看进程的内存信息,其中Bitmap相关的部分需要关注。 另外,内存泄漏的问题也是常见的。比如在Activity中加载大图,但没有及时回收,或者在列表中使用Bitmap没有正确复用,导致内存不断增长。这时候需要提到常见的泄漏场景和解决方法,比如使用弱引用、及时recycle(虽然现在可能不推荐直接调用,但需要说明版本差异),以及使用inBitmap属性来复用内存,特别是在列表或频繁创建Bitmap的场景下。 还有需要考虑的是不同屏幕密度下的内存问题。比如,图片放在不同的drawable目录下,系统会根据设备密度进行缩放,导致内存增加。例如,一张放在drawable-xxhdpi的图片在hdpi设备上会被放大,从而增加内存占用。这时候应该建议将图片放在合适的目录或者使用矢量图来避免这个问题。 另外,可能需要提到一些优化策略,比如使用合适的Bitmap.Config,根据需求调整图片尺寸(采样压缩),使用图片缓存库(如Glide、Picasso)来自动处理内存问题,以及如何正确管理Bitmap的生命周期,避免在不需要的时候仍然持有引用。 还有,在分析内存问题时,要考虑到不同Android版本之间的差异,比如之前提到的Native堆和Java堆的变化,以及API的兼容性处理。例如,在Android 8.0及以上版本,Bitmap内存又回到了Java堆,这可能影响到内存分析的方法和工具的使用。 总结下来,我需要分步骤地介绍Bitmap内存的组成部分、计算方法、分析工具、常见问题优化策略,确保用户能够系统地理解如何分析和处理Bitmap相关的内存问题。同时,要注意用词准确,避免技术错误,并给出实际的例子和建议,帮助用户更好地应用这些知识。</think>### 安卓 Bitmap 内存分析指南 #### 一、Bitmap 内存管理机制 1. **内存分布** - **Android 3.0 之前**:Bitmap 像素数据存储在 Java 堆,受 `GC` 影响大,易触发 `OOM`。 - **Android 3.0~7.x**:像素数据移至 Native 堆,Java 层仅保留引用,减少 `GC` 压力。 - **Android 8.0+**:像素数据回归 Java 堆(通过 `NativeAllocationRegistry` 管理),避免 Native 内存泄漏。 2. **内存计算公式** $$ \text{内存大小} = \text{宽度} \times \text{高度} \times \text{每像素字节数} $$ - 不同 `Bitmap.Config` 的每像素字节数: - `ARGB_8888`:4 字节 - `RGB_565`:2 字节 - `ALPHA_8`:1 字节 **示例**:一张 1920×1080 的图片使用 `ARGB_8888` 配置,内存占用为: $$ 1920 \times 1080 \times 4 = 8,294,400 \text{字节} \approx 7.91 \text{MB} $$ --- #### 二、内存分析工具 1. **Android Profiler** - 查看实时内存分配,定位 Bitmap 内存峰值。 - 支持堆转储(Heap Dump),分析 Bitmap 对象数量和占用空间。 2. **代码 API** ```kotlin val bitmapAllocationSize = bitmap.allocationByteCount // 获取实际分配内存 ``` 3. **命令行工具** ```bash adb shell dumpsys meminfo <package_name> # 输出中包含 "Graphics" 部分,显示 Bitmap内存 ``` --- #### 三、常见问题优化 1. **内存泄漏场景** - **未释放 Activity/Fragment 中的 Bitmap**:在 `onDestroy()` 中调用 `recycle()`(仅对 Native 堆有效)。 - **列表未复用 Bitmap**:使用 `RecyclerView` + `BitmapPool`(如 Glide 内置复用机制)。 2. **屏幕密度适配问题** - 图片放在低密度目录(如 `drawable-mdpi`)时,在高密度设备上会放大,导致内存增加。 - **解决方案**: - 将图片放在 `drawable-nodpi` 目录,禁止系统缩放。 - 使用矢量图(VectorDrawable)或 WebP 格式。 3. **优化策略** - **采样压缩**:通过 `inSampleSize` 降低解码后的尺寸。 ```kotlin val options = BitmapFactory.Options() options.inSampleSize = 2 // 宽高缩小为 1/2 ``` - **复用 Bitmap**:Android 4.4+ 使用 `inBitmap` 属性复用内存。 - **配置选择**:根据需求选择 `RGB_565`(无透明度需求)或 `ARGB_4444`。 - **图片库推荐**:Glide/Picasso 自动处理生命周期和内存复用。 --- #### 四、扩展注意事项 - **大图加载**:使用 `RegionDecoder` 分块加载局部区域。 - **Native 内存监控**:Android 8.0 前需关注 `adb shell procrank` 或 `DDMS` 的 Native 堆数据。 - **API 兼容性**:`inBitmap` 在 Android 4.4 前要求大小严格匹配,4.4+ 支持更大 Bitmap 复用。 --- 通过以上分析工具和优化方法,可有效定位并解决 Bitmap 内存问题,提升应用性能和稳定性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值