Glide是Google官方推荐的一款加载库,使用起来非常的简单便利,通常我们最简单的调用如下
但是在使用Glide时候,我们可以通过一些设置来优化内存占用,避免界面出现卡顿或者OOM, 例如:一个购物网站的商品详情页有好几十张超大图(运营配置的大图一般都是高清相机直接上传的,后台也没有做限制),这个时候当我们上下滑动商品详情页会很明显出现卡顿,或者是我们无限制的通过猜你喜欢打开很多个商品详情页,这个时候我们通过Android Studio自带Profiler内存检测工具可以很明显的看到我们的内存很快达到极限
我们使用最基础的方式加载一张大小为 4665600 byte 的
通过logger日志看看最终我们拿到的大小
默认情况下,色彩度为 ARGB_8888 ,每像素会占用 4 bytes 的大小。Glide V3使用 RGB_565 位图格式,它需要每个像素 2 bytes ,内存占用是 ARGB_8888 的一半, 最新的Glide V4默认使用的是 ARGB_8888 格式
我们通过下图看看 ARGB_8888 和 RGB_565 位图的区别
GlideModule是Glide提供的一个配置接口,它会在第一次使用Glide的时候被调用,用于进行Glide的一些初始配置
具体 GlideModule 的使用,可以参见官方文档:
githubcom/bumptech/gl…
修改GlideV4默认配置
修改Glide V3默认配置
使用RGB_565替代ARGB_8888的缺点
GlideV4使用RGB_565加载大图
通过logger日志看看最终我们拿到的大小
可以看到我们拿到的size比原图大小 4665600 byte 少了一半
当然,加载的位图的大小不仅取决于其质量,还取决于大小。 在ImageView中显示图像之前,Glide调整其大小,使其适合目标尺寸,以实现最佳的内存占用。 但是,有关Glide如何工作的一些事情值得了解,没必要将大位图加载到内存中。
我们通过logger日志看看最终我们拿到的大小
我们通过logger日志看看最终我们拿到的大小
我们可以看到, 我们的原图大小为 4665600 ,我们使用上述两种方式解决之后大小变为了 1492992
调整完的效果还是非常棒的,美女还是那样的漂亮,只有后边背景颜色有些许的减淡,但是我们大幅度降低内存占用,为了我们手机性能的大幅度提高这点质量上的损失还是可以接受的(除非UI设计师太苛刻)
Glide是一个很棒的库,可以轻松加载图像。 但是,有时候最好知道它如何以最有效的内存方式使用。 我希望我在本文中描述的事实将帮助您减少应用程序的内存占用,并避免一些邪恶的OOM错误。
参考文章
>
最近项目的IOS不能显示,但是Android的Glide框架加载又能显示,但是通过网页的形式请求发现如下格式:
感觉怪怪的。Glide这种url也能加载? >
阅读本文需要先了解 Glide加载流程
首先介绍一下Glide中对资源的封装类: EngineResource
在活动缓存中,使用了一个map用来存放EngineResource对象,这里需要注意一个 *** 作,就是这个EngineResource对象是用WeakReference包裹的,并且通过ReferenceQueue监听了EngineResource的回收,在回收的时候会清理当前的活动缓存内容;
下面分析一下源码是如果实现的:
首先,自定义一个WeakReference类,将key和resource传进入(用于在WeakReference回收的时候释放),传入一个ReferenceQueue对象,用于监听WeakReference回收
开启一个子线程,在循环中监听ReferenceQueue的返回值,通过这个返回值,判断WeakReference有没有回收,监听的方法是ReferenceQueueremove(),这是一个阻塞方法;所以要开子线程;
LruResourceCache继承了LruCache类,关于LruCache类,简单提一下,具体的可以参考我之前的博客 LruCache实现 ,LruCache继承了LinkedHashMap,LinkedHashMap有一个特点,就get后的数据会移动到队列,这就是Lru思想:固定一个容量,put的时候如果超过容量了,将最后一个节点删除,get的时候将get的这个节点移动到队列的头部;
onItemEvicted()方法是LruCache的一个空方法,调用的时机是在put的时候判断是否超过容量,如果超过容量了,就淘汰最后一个节点,并调用这个方法;
活动缓存和内存缓存都是缓存在内存中的,活动缓存缓存的是正在使用的资源,当不使用时会放到内存缓存中,提出活动缓存的目的:单一的内存缓存由于Lru的淘汰机制会导致加载不稳定
首先介绍一个磁盘缓存方案DiskLruCache(非Google官方编写,但获得官方认证),关于这个磁盘缓存方案的理解可以看郭林的这片文章:
Android DiskLruCache完全解析,硬盘缓存的最佳方案
从上一篇文章知道,Glide加载 *** 作是通过 Engine 来驱动的
Engine的load()中,首先尝试从 活动缓存 和 内存缓存 获取缓存,如果没有缓存再启动EngineJob和DecodeJob; 上面介绍了缓存的获取,下面看一下缓存的存放,肯定是在获取到后的回调中存放的
在DecodeJob获取到数据后,会回调很多接口,在回调中会将其放入 活动缓存 ,当不在使用的时候,就会放入内存缓存,根据上面介绍的活动缓存规则,当 EngineResource 计数为0时就应该放入内存缓存;
当资源引用为0,回调onResourceReleased(),从活动缓存移除,放入内存缓存;
上面介绍了活动缓存和内存缓存的存放和获取,下面看一看磁盘缓存的存取;
还记得 DataFetcherGenerator 接口吗?这个接口是DecodeJob用于获取数据的,有三个具体的实现:
我们在上一篇具体介绍的是网络文件的获取,这里的磁盘缓存使用的就是 DataCacheGenerator(缓存文件) 这个Generator了
上篇文章知道DecodeJob是一个Runnable任务,在run()会调用runWrapped(),在runWrapped()中会做三种事情:
在runWrapped()的解码 *** 作中会执行decode(),在decode()中,会disk put *** 作;
Glide的磁盘缓存是基于DiskLruCache 实现的,Glide直接使用的是DiskLruCacheWrapper对象对DiskLruCache 的封装;
摘抄自网上的一段解释:
alvik和ART都没有使用compacting garbage collector垃圾回收模式,这种模式中GC会遍历堆,同时把活跃对象移到相邻内存区域,让更大的内存块可以用在后续的分配中。因为安卓没有这种模式,就可能会出现被分配的对象分散在各处,对象之间只有很小的内存可用。如果应用试图分配一个大于邻近的闲置内存块空间的对象,就会导致OOM崩溃,即使总的空余内存空间大于要分配的对象的大小。
而且,同步GC时会暂停所有线程(包括UI主线程)以便进行GC,虽然暂停时间很短,但频繁的同步GC会造成页面卡顿,从而影响用户体验。
因此为了避免频繁的创建以及回收Bitmap对象,进而减少GC的出现,可以使用BitmapPool统一的管理Bitmap的创建以及重用。
因为Glide提供了LruBitmapPool方法,从而使用的加载进一步高效化,从上面的解释,简单概括就是避免Bitmap的频繁创建,提高复用,减少了内存抖动
内存抖动是由于短时间内有大量对象进出Young Generiation区导致的,它伴随着频繁的GC。
从图中大致可以了解到其LruBitmapPool调用流程(注意如果使用 into(target) 方法则不会使用LruBitmapPool机制),在位图转换的时候去查找LruBitmapPool中是否存在大小和配置信息相同的bitmap,有则返回
传入大小及配置信息来查找相匹配的位图
在调用LruBitmapPoolget方法获取到Bitmap后,通过如下方法将获取到的bitmap作为参数传给Canvas,在canvas中把inBitmap像素填充到进去,通过对象复用,很好的优化了内存抖动问题
adapter里面计算宽高,这个宽高可以让服务器获取当让我们也可以自己获取。
下面就说下实现方式吧
既然要动态适配宽高就要根据的宽度和手机的宽度计算出比率来然后根据这个比率来计算imageview的高度
package comjtechscrollimageloaddemo;
import androidappActivity;
import androidcontentContext;
import androidviewLayoutInflater;
import androidviewView;
import androidviewViewGroup;
import androidwidgetBaseAdapter;
import androidwidgetImageView;
import combumptechglideGlide;
import comjtechadapterRecyclerAdapter;
import comjtechviewRecyclerHolder;
import javautilArrayList;
import javautilList;
/
适配器
关于 同等间距的recycleView
我们在xml文件里设置的距离是dp 在代码里设置的距离是px
所以都在代码中设置统一格式就是同等编剧的recyclerview
Created by wuxubaiyang on 16/5/6
/
public class ImageAdapter extends RecyclerAdapter<ImageModel> {
private boolean isScroll = false;
private int itemWidth;
public ImageAdapter(Activity activity) {
super(activity);
//计算item的宽度
itemWidth = (DeviceUtilsgetScreenWidth(activity)-48) / 2;
}
public void setScroll(boolean scroll) {
isScroll = scroll;
if (!isScroll) {
notifyDataSetChanged();
}
}
@Override
public View createView(LayoutInflater layoutInflater, ViewGroup viewGroup, int i) {
return layoutInflaterinflate(Rlayoutview_item, viewGroup, false);
}
@Override
public void convert(RecyclerHolder recyclerHolder, ImageModel imageModel, int i) {
ImageView imageView = recyclerHoldergetView(Ridimageview);
//等比缩放
double ratio = (itemWidth 10) / imageModelgetWidth();
int height = (int) (imageModelgetHeight() ratio);
ViewGroupLayoutParams layoutParams = imageViewgetLayoutParams();
layoutParamswidth = itemWidth;
layoutParamsheight = height;
imageViewsetLayoutParams(layoutParams);
//显示
// if (isScroll) {
// imageViewsetImageResource(Rmipmapic_launcher);
// } else {
Glidewith(getActivity())load(imageModelgetUrl())placeholder(Rmipmapic_launcher)into(imageView);
// }
}
}
以上就是关于Glide如何优化内存消耗全部的内容,包括:Glide如何优化内存消耗、Glide多种组合使用方式、Glide 图片加载原理梳理等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)