2020 Android 大厂面试(一)图片 含 参考答案

2,529 阅读7分钟

一、图片

1、图片库对比
2、LRUCache原理
3、图片加载原理
4、自己去实现图片库,怎么做?
5、Glide源码解析
6、Glide使用什么缓存?
7、Glide内存缓存如何控制大小?

参考答案:

1、图片库对比

juejin.cn/post/684490…

Picasso Glide Fresco

Picasso 毕加索 Square

Picasso 没有实现本地缓存功能,交给了 Square 的另外一个网络库 okhttp 去实现,
这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。
  • 使用简单,代码简洁
  • 与Square其他类库搭配兼容性好,Retrofit OkHttp 等

缺点:

  • 功能简单 图片加载
  • 性能(加载速度等等)较(Glide、Fresco)差
  • 自身没有实现"本地缓存"

Glide Google 开源

支持 Gif WebP Video
生命周期继承
高效缓存策略
  • 支持Memory和Disk缓存
  • Picasso 只会缓存原始尺寸图片,而Glide缓存时多种规格
  • 内存开销小,默认 RGB_565,Picasso 默认是 ARGB_8888

缺点:

  • 使用方法复杂,实现方法较多
  • 使用比Fresco简单,但性能(加载&缓存)却比不上 Fresco

Fresco Facebook

  • 大大减少OOM,底层使用C++技术解决图片缓存问题
  • 使用加单,几乎全部功能都能在 xml 上定制

缺点:

  • 用法变得更加复杂
  • 依赖包更大了 2~3M
  • 底层C++,阅读源码困难

juejin.cn/post/684490…

对比项 Picasso Glide Fresco
地址 github.com/square/pica… github.com/bumptech/gl… github.com/facebook/fr…
发布时间 2013年5月 2014年9月 2015年5月
是否支持gif false true true
是否支持webP true true true
视频缩略图 false true true
大小 100k 500 KB 2~3M
加载速度
Disk+Men Cache true true true
Easy of use low mediun difficult
star 13160 14709 12444
开发者 Square主导 Google主导 Facebook主导

详细属性对比

对比项 Glide Fresco
配置 compile 'com.github.bumptech.glide:glide:XXX.XXX' compile 'com.facebook.fresco:fresco:XXX.XXX
初始化 直接使用 Fresco.initialize(this);
layout 普通ImageView 独有的SimpleDraweeView
圆角, 圆形 需要自己实现圆角,继承自BitmapTransformation操作bitmap对象实现 通过RoundingParams设置参数
缓存 Glide内存和磁盘缓存 三级缓存,分别是 Bitmap缓存,未解码图片缓存, 文件缓存。
缓存图像大小 Glide则会根据ImageView控件尺寸获得对应的大小的bitmap来展示,从而缓存也可以针对不同的对象:原始图像(source),结果图像(result) 缓存原始图像
加载策略 Glide只有占位图 先加载小尺寸图片,再加载大尺寸的
加载进度 false true
Bitmap myBitmap = Glide.with(上下文)  
    .load(url)  
    .asBitmap() //必须    
    .get()
//同样在DataSubscriber中获取
FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(url));
if (resource != null && resource.getFile() != null) {           
    setImage(ImageSource.uri(Uri.fromFile(resource.getFile())));
}

2、LRUCache原理

blog.csdn.net/u010983881/…

LruCache DiskLruCache

LruCache是Android 3.1所提供的一个缓存类DisLruCache目前在Android还不是Android SDK的一部分,但Android官方文档推荐使用该算法来实现硬盘缓存。

LinkedHashMap 它使用了一个双向链表来存储Map中的Entry顺序关系,这种顺序有两种,一种是LRU顺序,一种是插入顺序

put()重要的就是在添加过缓存对象后,调用trimToSize()方法,来判断缓存是否已满,如果满了就要删除近期最少使用的算法。

trimToSize()方法不断地删除LinkedHashMap中队头的元素,即近期最少访问的,直到缓存大小小于最大值

LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队头元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队尾。

3、图片加载原理

www.jianshu.com/p/1d0bfbdaa…

4、自己去实现图片库,怎么做?

blog.csdn.net/github_3713…

图片的同步加载
图片的异步加载
图片压缩
内存缓存
磁盘缓存
网络拉取

5、Glide源码解析

www.jianshu.com/p/9bb50924d…

RequestManager with(Context context)
RequestManager with(android.app.Activity)
RequestManager with(android.app.Fragment)
RequestManager with(android.support.v4.app.Fragment)
RequestManager with(android.support.v4.app.FragmentActivity)

无论使用什么参数,最终都会进入如下三个方法创建 RequestManager:

RequestManager fragmentGet(Context context, android.app.FragmentManager fm);
RequestManager supportFragmentGet(Context context, android.support.v4.app.FragmentManager fm);
RequestManager getApplicationManager(Context context);

结论:

  • Activity--FragmentManager--RequestManagerFragment--RequestManager,所以一个 Activity 对应一个 RequestManager
  • 一个 Fragment 对应一个 RequestManager
  • Activity 包含 Fragment,Fragment 包含 Fragment,若分别创建 Glide 请求是并不会只创建一个 RequestManager 的
  • 子线程发起 Glide 请求或传入对象为 ApplicationContext ,则使用全局单例的 RequestManager

创建 RequestBuilder 的 load 方法有很多:

RequestBuilder<Drawable> load(@Nullable Bitmap bitmap);
RequestBuilder<Drawable> load(@Nullable Drawable drawable);
RequestBuilder<Drawable> load(@Nullable String string);
RequestBuilder<Drawable> load(@Nullable Uri uri);
RequestBuilder<Drawable> load(@Nullable File file);
RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId);
RequestBuilder<Drawable> load(@Nullable URL url);
RequestBuilder<Drawable> load(@Nullable byte[] model);
RequestBuilder<Drawable> load(@Nullable Object model);

看看有这么多重载方法,没一个都代表不同的加载源。 除此之外还有两个特殊的方法:

RequestBuilder<File> downloadOnly();
RequestBuilder<File> download(@Nullable Object model);

再创建 RequestManager 时会先创建一个不可见的 Fragment ,通过 FM 加入到当前页面,用这个不可见的 Fragment 即可检测页面的生命周期。

Request 主要的实现类有三个:

SingleRequest
ThumbnailRequestCoordinator
ErrorRequestCoordinator

6、Glide使用什么缓存?

www.jianshu.com/p/62b7f990e…

BitmapPool 大小通过 MemorySizeCalculator 设置;
使用 LRU 算法维护 BitmapPool ;
Glide 会根据 Bitmap 的大小与 Config 生成一个 Key;
Key 也有自己对应的对象池,使用 Queue 实现;
数据最终存储在 GroupedLinkedMap 中;
GroupedLinkedMap 使用哈希表、循环链表、List 来存储数据。

7、Glide内存缓存如何控制大小?

blog.csdn.net/github_3713…

一种是Resource缓存,一类是Bitmap缓存。

Resource缓存: 图片从网络加载,将图片缓存到本地,当需要再次使用时,直接从缓存中取出而无需再次请求网络。

Glide在缓存Resource使用三层缓存,包括:

一级缓存:缓存被回收的资源,使用LRU算法(Least Frequently Used,最近最少使用算法)。当需要再次使用到被回收的资源,直接从内存返回。
二级缓存:使用弱引用缓存正在使用的资源。当系统执行gc操作时,会回收没有强引用的资源。使用弱引用缓存资源,既可以缓存正在使用的强引用资源,也不阻碍系统需要回收无引用资源。
三级缓存:磁盘缓存。网络图片下载成功后将以文件的形式缓存到磁盘中。

Bitmap缓存 通过Bitmap压缩质量参数:Glide默认使用RGB_565,比系统默认使用的ARGB_8888节省一半的资源,但RGB_565无法显示透明度。

Bitmap缓存算法:

在Glide中,使用BitmapPool来缓存Bitmap,使用的也是LRU算法。
当需要使用Bitmap时,从Bitmap的池子中取出合适的Bitmap,若取不到合适的,则再新创建。
当Bitmap使用完后,不直接调用Bitmap.recycler()回收,而是放入Bitmap的池子。

8.Fresco 源码分析

juejin.cn/post/684490…

初始化Fresco。
获取DataSource。
绑定Controller与Hierarchy。
从内存缓存/磁盘缓存/网络获取图片,并设置到对应的Drawable层。

缓冲缓存层:由BufferedDiskCache实现,提供缓冲功能。
文件缓存层:由DiskStorageCache实现,提供实际的缓存功能。
文件存储层:由DefaultDiskStorage实现,提供磁盘文件读写的功能。

DiskStorageCache:实现了FileCache接口与DiskTrimmable接口是缓存的主要实现类。
DefaultDiskStorage:实现了DiskStorage接口,封装了磁盘IO的读写逻辑。
BufferedDiskCache:在DiskStorageCache的基础上提供了Buffer功能。

private static final String CONTENT_FILE_EXTENSION = ".cnt"; private static final String TEMP_FILE_EXTENSION = ".tmp";

未解码图片内存缓存:由EncodedImage描述真正的缓存对象。 已解码图片内存缓存:由BitmapMemoryCache描述真正的缓存对象。

github.com/sucese/andr…