• 面试:Glide和Fresco图片框架对比


    图片框架优缺点整理(Fresco和Glide) - Tori Zhang

    性能优化专题三--内存优化(图片三级缓存)_沙漠一只雕得儿得儿的博客-CSDN博客

    Fresco优点:

    1. 网络和并请求:Fresco会自动复用相同URL的缓存,两个相同URL同时进行请求Fresco也只会进行一次网络请求,然后第二个进行复用。
    2. 在5.0以下系统,Fresco将图片放到一个特别的内存区域。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。在更底层的Native层对OOM进行处理,图片将不再占用App的内存
    3. 在加载gif图中,Fresco的java heap基本保持较低平稳状态,而Glide的java heap基本为Fresco的一倍。但是nativheap中Fresco高出很多
    4. 加载上:如动图加载,高斯模糊等常见的图片加载场景。另外还提供了独特的渐进式加载,先加载小图再加载大图
    5. 适用于需要高性能加载大量图片的场景

    Fresco问题:

    1. Fresco依赖的库很多
    2. 布局依赖SimpleDraweeView控件

    Glide优点:

    1. 有高效的缓存策略,支持原始图片和结果图片多种规格图片大小的缓存,而Picasso和Fresco只会缓存原始尺寸的图片,由于不需要再次处理图片大小,加载比较快
    2. 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求;方便处理bitmap,比如 Paused状态在暂停加载,在Resumed的时候又自动重新加载。)
    3. 加载速度在静态图上是比Fresco快的,在内存上,javaheap内存开销比Fresco大,但是NATIVE HEAP上不大。
    4. 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
    5. Glide 会根据你 ImageView 的大小来缓存相应大小的图片尺寸

    Glide问题:

    1. 相同URL的不同控件之间无法复用缓存。确认
    2. 在加载gif图时时间比较慢
    3. Glide加载的图片没有Picasso那么平滑
    4. Glide只有占位图

    对于一般App来说,Glide完全够用,而对于图片需求比较大的App,为了防止加载大量图片导致OOM,Fresco 会更合适一些。并不是说用Glide会导致OOM,Glide默认用的内存缓存是LruCache,内存不会一直往上涨。

    Glide图片加载流程

    图片加载描述

    在这里插入图片描述

    LruCache

    存在一个LinkedHashMap存放数据,并且实现了LRU(最少使用算法)缓存策略。

    Map<T,Y> cache = new LinkedHashMap<>(100,0.75f, true):
    • 其中第二个参数0.75f表示加载因子,即容量达到75%的时候会把内存临时增加一倍。
    • 最后这个参数也至关重要,表示访问元素的排序方式,true表示按照访问顺序排序,false表示按败插入的顺序排序。

    Android Glide 缓存机制及源码 - 知乎

    Glide缓存机制_橙子19911016的博客-CSDN博客_glide缓存机制

    二、假如让你自己写个图片加载框架,你会考虑哪些问题?

    下面就是我们的三级缓存机制:内存缓存、复用池、磁盘缓存 

    首先,梳理一下必要的图片加载框架的需求:

    • 使用inSampleSize采样率压缩,尺寸压缩
    • 合理选择Bitmap的像素格式
    • 异步加载:线程池
    • 切换线程:Handler,没有争议吧
    • 多级缓存:LruCache、对象复用池、DiskLruCache
    • 防止OOM:软引用、LruCache、图片压缩、Bitmap像素存储位置
    • 内存泄露:注意ImageView的正确引用,生命周期管理
    • 列表滑动加载的问题:加载错乱、队满任务过多问题

    2.1 异步加载:

    线程池,多少个?

    由于网络会阻塞,所以读内存和硬盘可以放在一个线程池,网络需要另外一个线程池,网络也可以采用Okhttp内置的线程池。

    读硬盘和读网络需要放在不同的线程池中处理,所以用两个线程池比较合适。

    Glide 必然也需要多个线程池,看下源码是不是这样

    1. public final class GlideBuilder {
    2. ...
    3. private GlideExecutor sourceExecutor; //加载源文件的线程池,包括网络加载
    4. private GlideExecutor diskCacheExecutor; //加载硬盘缓存的线程池
    5. ...
    6. private GlideExecutor animationExecutor; //动画线程池

    Glide使用了三个线程池,不考虑动画的话就是两个。

    2.2 切换线程:

    图片异步加载成功,需要在主线程去更新ImageView,

    无论是RxJava、EventBus,还是Glide,只要是想从子线程切换到Android主线程,都离不开Handler。

    2.3 缓存

    性能优化专题三--内存优化(图片三级缓存)_沙漠一只雕得儿得儿的博客-CSDN博客

    我们常说的图片三级缓存:内存缓存、复用池、磁盘缓存 

    内存缓存:可以使用官方已经帮我们实现好的LurCache;

    复用池:用来装载内存缓存LRU中被抛弃掉的,但是可能又会马上使用到的那些图片,我们在复用池中缓存下来,防止立马被GC回收掉;

    硬盘缓存:有JakeWharton大神实现的DiskLruCache,下载地址:

    为什么要设置复用池呢?

    因为在官方8.0Android系统中,将图片放在native层中进行了处理,例如回收机制,在java层我们无法进行干预,为了能够不立即将图片交给native层处理,我们利用复用池中的弱引用暂时保存在java层中,以方便我们需要使用刚刚被内存缓存LRU放弃掉的图片,而不是重复decode图片再放入到LRU队列中,降低性能消耗。

    下面就是我们的三级缓存机制:内存缓存、复用池、磁盘缓存

    LruCache 采用最近最少使用算法,设定一个缓存大小,当缓存达到这个大小之后,会将最老的数据移除,避免图片占用内存过大导致OOM。

    加载图片非常重要的一点是需要防止OOM,上面的LruCache缓存大小设置,可以有效防止OOM,但是当图片需求比较大,可能需要设置一个比较大的缓存,这样的话发生OOM的概率就提高了,那应该探索其它防止OOM的方法。

    方法1:软引用

    软引用的设计就是应用于会发生OOM的场景,大内存对象如Bitmap,可以通过 SoftReference 修饰,防止大对象造成OOM

    LruCache里存的是软引用对象,那么当内存不足的时候,Bitmap会被回收,也就是说通过SoftReference修饰的Bitmap就不会导致OOM。

    方法2:onLowMemory

    当内存不足的时候,Activity、Fragment会调用onLowMemory方法,可以在这个方法里去清除缓存,Glide使用的就是这一种方式来防止OOM。

    1. //Glide
    2. public void onLowMemory() {
    3. clearMemory();
    4. }
    5. public void clearMemory() {
    6. // Engine asserts this anyway when removing resources, fail faster and consistently
    7. Util.assertMainThread();
    8. // memory cache needs to be cleared before bitmap pool to clear re-pooled Bitmaps too. See #687.
    9. memoryCache.clearMemory();
    10. bitmapPool.clearMemory();
    11. arrayPool.clearMemory();
    12. }

    方法3:从Bitmap 像素存储位置考虑

    象的分配一般都是在堆中,堆是JVM中最大的一块内存,OOM一般都是发生在堆中。

    Bitmap 之所以占内存大不是因为对象本身大,而是因为Bitmap的像素数据, Bitmap的像素数据大小 = 宽 * 高 * 1像素占用的内存。

    如果Bitmap使用 RGB_565 格式,则1像素占用 2 byte,ARGB_8888 格式则占4 byte。
    在选择图片加载框架的时候,可以将内存占用这一方面考虑进去,更少的内存占用意味着发生OOM的概率越低。 Glide内存开销是Picasso的一半,就是因为默认Bitmap格式不同。

    那是否可以让像素数据不放在java堆中,而是放在native堆中呢?据说Android 3.0到8.0 之间Bitmap像素数据存在Java堆,而8.0之后像素数据存到native堆中

    2.4 ImageView 内存泄露

    将ImageView用WeakReference修饰就完事了。事实上,这种方式虽然解决了内存泄露问题,但是并不完美,例如在界面退出的时候,我们除了希望ImageView被回收,同时希望加载图片的任务可以取消,队未执行的任务可以移除。

    Glide的做法是监听生命周期回调,看 RequestManager 这个类

    1. public void onDestroy() {
    2. targetTracker.onDestroy();
    3. for (Target target : targetTracker.getAll()) {
    4. //清理任务
    5. clear(target);
    6. }
    7. targetTracker.clear();
    8. requestTracker.clearRequests();
    9. lifecycle.removeListener(this);
    10. lifecycle.removeListener(connectivityMonitor);
    11. mainHandler.removeCallbacks(addSelfToLifecycle);
    12. glide.unregisterRequestManager(this);
    13. }

    在Activity/fragment 销毁的时候,取消图片加载任务

    面试官:简历上最好不要写Glide,不是问源码那么简单 - 掘金

  • 相关阅读:
    neo4j 图数据库初步调研 图数据库与关系型数据库差异-f
    Kafka - 13 Java 客户端实现消费者消费消息
    clip-path图片裁剪
    XPS表面及表面分析技术-科学指南针
    软考高项——各输入输出文件的含义
    React -三种数据通信方法都怎么用?什么时候用?
    【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.6 定时器事件
    华为交换机设置边缘端口(收到BPDU包后进入error-down状态)
    OpenCV-Python实战(1) —— 给图片添加图片水印【利用 OpenCV 像素的读写原理实现】
    PrintWrter中的write()和print()方法
  • 原文地址:https://blog.csdn.net/cpcpcp123/article/details/127831502