那些我们用过的Android开源图片加载框架

一、UniversalImageLoader

https://github.com/nostra13/Android-Universal-Image-Loader

UIL可以算是老牌最火的图片加载库了,使用过这个框架的项目可以说多到教你做人,我第一次把第三方开源图片加载框架加入项目中的就是这个了,当时感觉瞬间逼格上涨,妈妈再也不用担心出现OOM和ListView图片错乱了。可惜的是该作者在项目中说明已经停止了对该项目的维护。这就意味着以后任何的 bug 都不会修复,任何的新特性都不会再继续开发,所以毫无疑问 UIL 不推荐在项目中使用了。

使用方法:

1、在Application全局变量中的进行配置ImageLoaderConfiguration,有选择性的进行配置,具体代码如下:

ImageLoaderConfiguration config =newImageLoaderConfiguration.Builder(context)

.memoryCacheExtraOptions(480, 800)// default = device screen dimensions

.discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75)

.taskExecutor(AsyncTask.THREAD_POOL_EXECUTOR)

.taskExecutorForCachedImages(AsyncTask.THREAD_POOL_EXECUTOR)

.threadPoolSize(3)// default线程池数量

.threadPriority(Thread.NORM_PRIORITY - 1)// default

.tasksProcessingOrder(QueueProcessingType.FIFO)// default

.denyCacheImageMultipleSizesInMemory()

.memoryCache(newLruMemoryCache(2 * 1024 * 1024))//内存缓存

.memoryCacheSize(2 * 1024 * 1024)

.discCache(newUnlimitedDiscCache(cacheDir))// 磁盘缓存

.discCacheSize(50 * 1024 * 1024)

.discCacheFileCount(100)

.discCacheFileNameGenerator(newHashCodeFileNameGenerator())// default

.imageDownloader(newBaseImageDownloader(context))// default

.imageDecoder(newBaseImageDecoder())// default

.defaultDisplayImageOptions(DisplayImageOptions.createSimple())// default

.enableLogging()

.build();

2、针对每次加载任务进行配置DisplayImageOptions

DisplayImageOptions options =newDisplayImageOptions.Builder()

.showStubImage(R.drawable.ic_stub)

.showImageForEmptyUri(R.drawable.ic_empty)

.showImageOnFail(R.drawable.ic_error)

.resetViewBeforeLoading()

.delayBeforeLoading(1000)

.cacheInMemory()

.cacheOnDisc()

.preProcessor(...)

.postProcessor(...)

.extraForDownloader(...)

.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)// default

.bitmapConfig(Bitmap.Config.ARGB_8888)// default

.decodingOptions(...)

.displayer(newSimpleBitmapDisplayer())// default

.handler(newHandler())// default

.build();

UIL支持的图片加载格式如下:

String imageUri ="http://site.com/image.png"; // from Web

String imageUri ="file:///mnt/sdcard/image.png"; // from SD card

String imageUri ="content://media/external/audio/albumart/13"; // from content provider

String imageUri ="assets://image.png"; // from assets

String imageUri ="drawable://"+ R.drawable.image; // from drawables (only images, non-9patch)

配置好后调用imageLoader.displayImage方法就OK了,妥妥的!

下面简单分析一下,UIL框架的加载原理:

1、ImageLoader图片加载器,对外的主要 API,采取了单例模式,用于图片的加载和显示。

2、MemoryCache图片内存换成。默认使用了 LRU 算法。 LRU: Least Recently Used 近期最少使用算法, 选用了基于链表结构的 LinkedHashMap 作为存储结构。假设情景:内存缓存设置的阈值只够存储两个 bitmap 对象,当 put 第三个 bitmap 对象时,将近期最少使用的 bitmap 对象移除。

3、DiskCache图片磁盘缓存,默认使用LruDiskCache算法,在缓存满时删除最近最少使用的图片;缓存目录下名为journal的文件记录缓存的所有操作

4、图片加载流程

1.判断图片的内存缓存是否存在,若存在直接执行步骤 8;

2.判断图片的磁盘缓存是否存在,若存在直接执行步骤 5;

3.ImageDownloader从网络上下载图片;

4.将图片缓存在磁盘上;

5.ImageDecoder将图片 decode 成 bitmap 对象;

6.BitmapProcessor根据DisplayImageOptions配置对图片进行预处理(Pre-process Bitmap);

7.将 bitmap 对象缓存到内存中;

8.根据DisplayImageOptions配置对图片进行后处理(Post-process Bitmap);

9.执行DisplayBitmapTask将图片显示在相应的控件上。

二、Picasso

https://github.com/square/picasso

Picasso是Square公司开源的一个Android平台上的图片加载框架,简单易用,一句话搞定项目中的图片加载,好用到令人发指。

使用一句话:Picasso.with(this).load("url").placeholder(R.mipmap.ic_default).into(imageView);

原理简要分析:

1、Picasso.with(Context):入手

public static Picasso with(Contextcontext){

           if(singleton==null){

                   synchronized(Picasso.class){

                      if(singleton==null){

                         singleton=newBuilder(context).build();

                      }

                   }

           }

return singleton;}

单列模式,保证多线程情况下,也只有一个实例。

/** Create the {@link Picasso} instance. 创建Picasso的实例 */

public Picassobuild(){

Context context=this.context;

if(downloader==null){

downloader=Utils.createDefaultDownloader(context);

}

if(cache==null){

cache=new LruCache(context);

}

if(service==null){

service=new PicassoExecutorService();

}

if(transformer==null){

transformer=RequestTransformer.IDENTITY;

}

Stats stats=newStats(cache);

Dispatcher dispatcher=new Dispatcher(context,service,HANDLER,downloader,cache,stats);

return new Picasso(context,dispatcher,cache,listener,transformer,

requestHandlers,stats,indicatorsEnabled,loggingEnabled);

}

默认初始化了以下的参数:

Downloader

DownLoader就是下载用的工具类,在Picasso当中,如果OKHttp可以使用的话,就会默认使用OKHttp,如果无法使用的话,就会使用UrlConnectionDownloader(默认使用HttpURLConnection实现)。

Cache

默认实现为LruCache,就是使用LinkedHashMap实现的一个Cache类,注意的一个地方就是,在其他的地方,我们一般默认的是限制的capacity,但是这个地方我们是限制的总共使用的内存空间。因此LruCache在实现的时候,其实简单理解就是将LinkedHashMap封装,然后基于LinkedHashMap的方法实现Cache的方法,在Cache的set()方法的时候,会不断计算当前还可以使用的空间大小,要是超出范围,则删除之前保存的数据。

ExecutorService

默认的实现为PicassoExecutorService,该类也比较简单,其实就是ThreadPoolExecutor,在其功能的基础上继续封装,在其中有一个比较细心的功能就是,Picasso通过PicassoExecutorService设置线程数量,来调整在2G/3G/4G/WiFi不同网络情况下的不同表现。

RequestTransformer

ReqeustTransformer是一个接口,用来预处理Reqeust,可以用来将请求进行预先处理,比如改个域名啥的。

Stats

主要是一些统计信息,比如cache hit/miss,总共下载的文件大小,下载过的图片数量,转换的图片数量等等。

Dispatcher

Picasso当中,分发任务的线程,这是我们以后要重点研究的一个类,先标记一下,这个Dispatcher主要做了以下的事情:

启动了一个DispatcherThread线程初始化了一个用来处理消息的DispatcherHandler,注意,根据Dispatcher中默认配置,该Handler所有数据的处理是在DispatcherThread之上。初始化并注册了一个网络状态广播接收器。


2、图片加载流程:

1.初始化Picasso,实例化其唯一的对象。

2.根据传入的Url、File、resource Id,构建ReqeustCreator对象

3.根据ReqeustCreator构建Request对象,同时根据Reqeust属性,尝试从Cache中访问数据

4.Cache Hit,则通过回调,设置Target或者ImageView,完成该Reqeust

5.如果Cache Miss,那么则构建相应的Action,并提交到DispatcherThread当中。

6.Dispatcher中的Handler接收到相应的Message,调用dispatcher.performSubmit(action)进行处理。

7.创建BitmapHunter对象,并提交到PicassoExecutorService线程池

8.再次检查Memory Cache中已经有缓存,如果Hit,则读取缓存中的Bitmap

9.如果Cache miss,则交给Action对应的ReqeustHandler进行处理,比如网络请求,或者从File读取图片

10.返回结果之后,通知Dispatcher中的Handler处理结果。

11.DispatcherThread中将BitmapHunter的结果打包(batch),最快200ms打包一次。通知主线程HANDLER进行处理

12.主线程HANDLER接收打包的BitmapHunter,对最后的结果进行分发。

注意:Picasso框架没有实现磁盘缓存,配合OkHttp进行实现。

三、Glide

https://github.com/bumptech/glide

Glide 是 Google 一位员工的大作,他完全是基于 Picasso 的,沿袭了 Picasso 的简洁风格,但是在此做了大量优化与改进。

Glide 默认的 Bitmap 格式是 RGB_565 格式,而 Picasso 默认的是 ARGB_8888 格式,这个内存开销要小一半。

在磁盘缓存方面,Picasso 只会缓存原始尺寸的图片,而 Glide 缓存的是多种规格,也就意味着 Glide 会根据你 ImageView 的大小来缓存相应大小的图片尺寸,比如你 ImageView 大小是200*200,原图是 400*400 ,而使用 Glide 就会缓存 200*200 规格的图,而 Picasso 只会缓存 400*400 规格的。这个改进就会导致 Glide 比 Picasso 加载的速度要快,毕竟少了每次裁剪重新渲染的过程。

最重要的一个特性是 Glide 支持加载 Gif 动态图,而 Picasso 不支持该特性。

除此之外,还有很多其他配置选项的增加。

总体来说,Glide 是在 Picasso 基础之上进行的二次开发,各个方面做了不少改进,不过这也导致他的包比 Picasso 大不少,不过也就不到 500k,Picasso 是100多k,方法数也比 Picasso 多不少,不过毕竟级别还是蛮小的,影响不是很大。

四、Fresco

https://github.com/facebook/fresco


Fresco 是 Facebook 出品,他是新一代的图片加载库,我们知道 Android 应用程序可用的内存有限,经常会因为图片加载导致 OOM,虽然我们有各种手段去优化,尽量减少出现 OOM 的可能性,但是永远没法避免,尤其某些低端手机 OOM 更是严重。而 Facebook 就另辟蹊径,既然没法在 Java 层处理,我们就在更底层的 Native 堆做手脚。于是 Fresco 将图片放到一个特别的内存区域叫 Ashmem 区,就是属于 Native 堆,图片将不再占用 App 的内存,Java 层对此无能为力,这里是属于 C++ 的地盘,所以能大大的减少 OOM。

本人四个库都使用了一遍,对比到Fresco确实强大,加载大图Fresco最屌,有的图Glide和Picasso加载不出来,换上Fresco妥妥的,不过Fresco比较庞大,推荐在主要都是图片的app中使用,一般的app使用Glide和Picasso就够了!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容