1.前言Glide是Google官方推荐的图片加载库。使用起来也非常简单方便。Glide帮助我们完成很多重要但常用的功能,例如:图片加载压缩、显示、加载图片的内存管理等。不熟悉Glide的朋友可以参考《一篇好文,助你上手 Glide》,不过在使用Glide的时候,有一些小技巧可以优化您的内存并避免可能的OOM。例如:虽然Glide会根据加载控件的大小来优化加载图片的大小,但是如果加载一个大的全屏图片,仍然会是一个占用大量内存空间的操作。一个Bitmap占用多少内存空间,可以参考《Bitmap 比你想的更费内存 | 吊打 OOM》本文部分建议来自AndroidTVApp,AndroidTV有很多智能电视和智能盒子,但硬件条件其实很差,而且AndroidTVApp,为了美化会使用大部分的图片,所以OOM的问题会在图片的使用上被放大,下面介绍的一些优化方案,在Android手机硬件条件很好的环境下,不会如果不使用它们会产生很大的影响。2.开始优化2.1配置TrimMemory和LowMemoryGlide来帮我们做大部分的内存管理。事实上,它支持做得更好。对于一个app来说,当系统内存环境不足时,会回调一些方法,比如onTrimMemory()或者onLowMemory()。这些都是为了提醒开发者当前设备的内存环境发生了变化。***调整你的内存使用策略,避免被系统清理或OOM。关于onTrimMemroy()的内容,不了解的可以参考《Android 开发,跳不过的内存管理》,Glide也为我们提供了类似方法的接口。开发者只需要调用它。它将帮助我们调整Cachedimages进行优化。这里主要用到Glide的trimMemory()和cleanMemroy()方法,其中一个用于裁剪Glide缓存的图片内存空间,另一个用于清理Glide缓存的内存空间。在使用onTrimMemory()之前,一般先实现ComponentCallbacks2接口,然后通过registerComponentCallbacks()方法在Application中注册。当然,如果嫌麻烦,也可以直接在Application中重写相应的方法。知道了这些,我们就可以根据自己的需要来配置什么时候调用Glide对应的方法了。我推荐的配置:当lowMemory时,调用Glide.cleanMemroy()清理所有内存缓存。当App被替换到后台时,调用Glide.cleanMemroy()清理所有内存缓存。其他情况在onTrimMemroy()回调中,直接调用Glide.trimMemory()方法交给Glide处理内存情况。那么对应的代码如下:既然我们知道需要调用Glide的这两个方法,那么我们还需要了解它内部为我们做了什么。我们先来看一下Glide对应的源码。在Glide的这些方法中,可以看到它们会操作memoryCache和bitmapPool这两个对象。实际上,它们是两个接口。这里如果做特殊处理,Glide默认对它们的实现,LruResourceCache和LruResourceCacheLruBitmapPool。从名字可以看出,它们都是遵循Lru算法。就Glide而言,MemoryCache是??Glide使用的将图片资源缓存在内存中,以便在需要使用时可以立即使用,而无需进行磁盘I/O操作,而BitmatPool是Glide使用来维护图片资源。使用pool,LruBitmapPool使用Lru算法来保持最近使用过的Bitmap大小,这个不是本文的重点,大家了解即可。其实在LruResourceCache和LruBitmapPool中,对clearMemory()和trimMemory()的操作是类似的,这里以LruBitmapPool为例。在LruBitmapPool中,会根据回调方法和参数调用clearMemory()或trimToSize()。实际上,最终调用了trimToSize()方法。它用于修剪当前缓存资源的数量。可以看出,根据裁剪的目标大小,将多余的Bitmap回收到合适的目标大小,达到清理内存的目的。2.2配置GlideModuleGlideModule是Glide提供的配置接口,在第一次使用Glide时会被调用,用于Glide的一些初始配置。GlideModule的具体使用可以参考官方文档:https://github.com/bumptech/glide/wiki/ConfigurationGlideModule是一个接口,需要实现其对应的方法。这里我们只需要使用applyOptions()方法,该方法用于在Glide默认配置的基础上添加一些我们需要的配置。而这里,我们可以根据当前设备的内存进行设置,使用ActivityManager获取当前设备的内存,如果是lowMemory,则将图片的DecodeFormat设置为RGB_565,RGB_565与默认的ARGB_8888进行比较,每个像素将减少2个字节。这样就相当于是同一张图片,加载到内存时内存占用会减少一半(ARGB_8888每个像素占4个字节)。2.3避免使用带圆角的ImageView在实际项目中,经常会用到一些带有圆角的图片,或者直接使用圆形图片。圆形图片多用于某些用户的头像等展示效果。在Android下,也有大量类似于XxxImageView的开源控件,用于操作Bitmap来实现图片圆角的效果,比如Github上流行的RoundedImageView。大部分的原理是接收你传过来的Bitmap,然后输出一个和原来Bitmap一样大小的新Bitmap。其中,如果多持有一张Bitmap,一张图片占用的内存就会翻倍。所以既然选择了使用Glide,那么推荐使用开源库glide-transformations。Glide-transformations使用Glide的bitmapTransfrom()接口对加载的Bitmap进行一些变换操作。glide-transformations的Github地址如下:https://github.com/wasabeef/glide-transformationsglide-transformations提供了一系列对加载图片的变换操作,从形状变换到颜色变换,都支持,基本满足大部分开发需要,它会复用Glide的BitmapPool来达到节省内存的目的。glide-transformations的具体使用可以查看Github上的文档,下面是效果图。2.4根据内存情况裁剪图片。上面介绍的一些优化点是一些推荐的通用做法。基本上使用上面介绍的方法应该可以大大减少图片引起的OOM。接下来介绍一个优化AndroidTV加载全屏大图时内存问题的解决方案。首先必须明确的是,AndroidTV在国内的硬件环境非常糟糕。230个智能盒子到处卖。也不要期望太多。但是AndroidTV是为电视而生的,所以在大多数情况下,它需要支持1920*1280这样的屏幕尺寸,所以如果加载一个大的全屏图像,消耗的内存是难以承受的。如果内存环境不好,可能会直接OOMcrash。于是,针对这种极端情况,我想到了一个办法,根据当前的内存环境,将需要显示的全屏图片缩小,让加载到内存中的图片缩小。这里需要用到DrawableRequestBuilder的override()API,它可以接受一个宽高来重新指定加载图片的尺寸。现在Glide已经提供了标准的API,我们还需要获取当前运行设备的宽高。推荐使用getRealSize()来获取屏幕的宽高,可以实际获取到当前屏幕的大小。其他Api在一些智能电视和盒子上,获取的尺寸会比较小,因为没有计算StatusBar或者NavigationBar的高度,这些都是经验。同时我们还需要用到ComponentCallbacks2接口,之前已经介绍过,这里不再赘述。在其中记录trimlevel的值,以反映当前的内存级别。在使用的时候,通过getBitmapSize()切出一个适合当前内存环境的大小。例子中只处理了TRIM_MENORY_RUNNING_LOW,会根据屏幕尺寸缩放到0.8f倍。如果你想做更多,你还可以添加其他几个级别来调整不同的缩放因子。输出两者看区别,同一张全屏图片,unscaled和scaled相差0.8f。I/cxmyDev:bgImagebyteCount:8294400I/cxmyDev:bgImagebyteCount:5308416可以看到,优化的目的已经达到。可节省约3MB内存空间,画面不会模糊到看不清的地步。3.总结优化永无止境。今天先说到这里,想到了再补充。如果大家有更好的建议,可以在文末留言一起讨论。iOS长按提示
