Android开发——内存优化 图片处理

简介:

8.  用缓存避免内存泄漏

很常见的一个例子就是图片的三级缓存结构,分别为网络缓存,本地缓存以及内存缓存。在内存缓存逻辑类中,通常会定义这样的集合类。

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. private HashMap<String, Bitmap> mMemoryCache = new HashMap<String, Bitmap>();//String类为该图片对应url  

三级缓存结构过程介绍:

在用户切换到展示图片的界面时,当然是优先判断内存缓存是否为Null,不为空直接展示图片,若为空,同样的逻辑去判断本地缓存(不为空便设置内存缓存并展示图片),本地缓存再为空才会根据该图片的url用网络下载类去下载该图片并展示图片(当然了,下载到图片后会有设置本地缓存以及内存缓存的操作)。

内存泄漏的问题就出现在内存缓存中:只要HashMap对象实例被引用,而Bitmap对象又都是强引用,Bitmap中图片越来越多,即便是内存溢出了,垃圾回收器也不会处理。

 

解决方案:

1)我们可以选择使用软引用,从而在内存不足时,垃圾回收器更容易回收Bitmap垃圾。

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<String, SoftReference<Bitmap>>();  

2Android2.3以后,SoftReference不再可靠。垃圾回收期更容易回收它,不再是内存不足时才回收软引用。那么缓存机制便失去了意义。

Google官方建议使用LruCache作为缓存的集合类。其实内部封装了LinkedHashMap。内部原理是一直判断集合大小是否超出给定的最大值,超出就把最早最少使用的对象踢出集合。

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. private LruCache<String, Bitmap> mMemoryCache = new LruCache<String, Bitmap>  
  2. ((int)(Runtime.getRuntime().maxMemory()/8)){   
  3. //用最大内存的1/8分配给这个集合使用  
  4. //让这个集合知道每个图片的大小  
  5. @Override  
  6. protected int sizeOf(String key, Bitmap value){  
  7. int byteCount = value.getRowBytes() * value.getHeight();//计算图片大小,每行字节数*高度  
  8. return byteCount;  
  9.   }  
  10. };   

 

9.  优化Bitmap避免内存泄漏

Android中很多控件比如ListView/GridView/ViewPaper通常都会包含很多图片,特别是快速滑动的时候可能加载大量的图片,因此对图片进行优化处理显得尤为重要。

 

9.1  图片质量压缩

 

[java]  view plain  copy
  1. public static Bitmap compressImage(Bitmap bitmap){    
  2.     ByteArrayOutputStream baos = new ByteArrayOutputStream();    
  3.     //质量压缩方法,参数100表示不压缩,把压缩后的数据存放到baos中    
  4.     bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);    
  5.     int options = 100;    
  6.     //循环判断如果压缩后图片大小>50kb就继续压缩    
  7.     while ( baos.toByteArray().length/1024 > 50) {    
  8.          //清空baos    
  9.          baos.reset();    
  10.          bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);    
  11.          options -= 10;//每次都减少10    
  12.     }    
  13.     //把压缩后的数据baos存放到ByteArrayInputStream中    
  14.     ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());    
  15.     //把ByteArrayInputStream数据生成图片    
  16.     Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);    
  17.     return newBitmap;            
  18. }  

 

9.2  图片尺寸裁剪

如果说没有裁剪,下载下来是200*200,而ImageView本身是100*100的,具体原因可以参考这篇文章,这样就浪费了一部分内存。

使用BitmapFactory.Options设置inSampleSize就可以缩小图片。如果值为2,则缩略图的宽和高都是原始图片的1/2,图片的大小就为原始大小的1/4(小于等于1不缩放)。具体方法如下:

 

既然有了inSampleSize的概念,我们就要对比实际图片大小和ImageView控件的大小,如果使用内存直接处理实际图片的Bitmap从而得到实际大小的话,就失去了图片尺寸裁剪的意义,因为内存已经被消耗了。因此BitmapFactory.Options提供了inJustDecodeBounds标志位,当它被设置为true后,再使用decode系列方法时,并不会真正的分配内存空间,这样解码出来的Bitmapnull但是可以计算出原始图片的真实宽高,即options.outWidthoptions.outHeight。通过这两个值,就可以知道图片是否过大了。

 

 

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. BitmapFactory.Options options = new BitmapFactory.Options();    
  2. options.inJustDecodeBounds = true;    
  3. BitmapFactory.decodeResource(getResources(), R.id.myimage, options);    
  4. int imageHeight = options.outHeight;    
  5. int imageWidth = options.outWidth;    
  6. String imageType = options.outMimeType;   

 

这里提供了一个calculateInSampleSize()工具方法来帮我们根据实际情况动态计算合适的inSampleSize

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. public static int calculateInSampleSize( //参2和3为ImageView期待的图片大小  
  2.             BitmapFactory.Options options, int reqWidth, int reqHeight) {    
  3.     // 图片的实际大小  
  4.     final int height = options.outHeight;    
  5.     final int width = options.outWidth;    
  6.     //默认值  
  7.     int inSampleSize = 1;    
  8.     //动态计算inSampleSize的值  
  9.     if (height > reqHeight || width > reqWidth) {    
  10.         final int halfHeight = height/2;  
  11.         final int halfWidth = width/2;  
  12.         while( (halfHeight/inSampleSize) >= reqHeight && (halfWidth/inSampleSize) >= reqWidth){  
  13.             inSampleSize *= 2;  
  14.         }  
  15.     }    
  16.     return inSampleSize;    
  17. }    

 

创建一个完整的缩略图方案:

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,    
  2.         int reqWidth, int reqHeight) {    
  3.   
  4.     final BitmapFactory.Options options = new BitmapFactory.Options();    
  5.     options.inJustDecodeBounds = true;    
  6.     BitmapFactory.decodeResource(res, resId, options);    
  7.     
  8.     // 计算inSampleSize,因为前面已经设置过标志位并调用了decode方法,所以参数option包含了真实宽高信息  
  9.     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);    
  10.     
  11.     // 别忘记将opts.inJustDecodeBound设置回false,否则获取的bitmap对象还是null   
  12.     options.inJustDecodeBounds = false;    
  13.     //重新加载图片  
  14.     return BitmapFactory.decodeResource(res, resId, options);    
  15. }    

 

当我们在使用ImageView进行设置图片资源时:

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. mImageView.setImageBitmap( //ImageView所期望的图片大小为100*100像素  
  2.     decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));    

 

9.3  改变图片颜色模式


Android默认的颜色格式是ARGB_8888,在不要求透明度的情况下可以改成RGB_565,这样每个像素占用的内从可从4byte将为2byte。

 

一张分辨率为1920x1080的图片,如果Bitmap使用ARGB_8888格式显示的话,占用的内存将是1920x1080x4个字节,将近8M内存。

 

10.  及时回收资源

 

(1)当界面不可见时我们应当将所有和界面相关的资源进行释放。

我们可以在Activity中重写onTrimMemory()方法,通过switch这个方法中的level参数,判断它是不是等于TRIM_MEMORY_UI_HIDDEN,就说明用户已经离开了我们的程序,此时就可以进行UI相关资源释放操作了,如下所示:

[java]  view plain  copy 在CODE上查看代码片派生到我的代码片
  1. @Override    
  2. public void onTrimMemory(int level) {    
  3.     super.onTrimMemory(level);    
  4.     switch (level) {    
  5.     case TRIM_MEMORY_UI_HIDDEN:    
  6.         // 进行资源释放操作    
  7.         break;    
  8.     }    
  9. }    

比如Android3.0开始支持的属性动画中有一类无限循环的动画,它会通过View间接持有Activity的引用,如果没有在onDestroy中停止动画(animator.cancel()),就会泄漏当前的Activity。说起动画,还有一点就是减少帧动画的使用。

(2)Google也建议在onStop()方法中释放资源,但是和上面的释放UI资源是有区别的,因为onStop()方法只是当一个Activity不可见的时候就会调用,比如说用户打开了我们程序中的另一个ActivityB。在onStop()方法中适合去关闭一些读写文件的资源、数据库操作相关的资源等等

但是像UI相关的资源应该一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)这个回调之后才去释放,否则从ActivityB回到ActivityAUI相关的资源会重新加载。

 

至此关于Android内存泄漏的内容总结完毕。




    本文转自 一点点征服   博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6692174.html,如需转载请自行联系原作者

相关文章
|
20天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
25天前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
1天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
4 0
|
3天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
11天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
20天前
|
Android开发
Android开发小技巧:怎样在 textview 前面加上一个小图标。
Android开发小技巧:怎样在 textview 前面加上一个小图标。
10 0
|
20天前
|
Android开发
Android 开发 pickerview 自定义选择器
Android 开发 pickerview 自定义选择器
12 0
|
25天前
|
缓存 移动开发 Java
构建高效Android应用:内存优化实战指南
在移动开发领域,性能优化是提升用户体验的关键因素之一。特别是对于Android应用而言,由于设备和版本的多样性,内存管理成为开发者面临的一大挑战。本文将深入探讨Android内存优化的策略和技术,包括内存泄漏的诊断与解决、合理的数据结构选择、以及有效的资源释放机制。通过实际案例分析,我们旨在为开发者提供一套实用的内存优化工具和方法,以构建更加流畅和高效的Android应用。
|
29天前
|
存储 JSON 监控
Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
【2月更文挑战第30天】Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
14 1
|
1月前
|
存储 C语言
C语言--------数据在内存中的存储
C语言--------数据在内存中的存储
26 0

热门文章

最新文章