目前Android 发展至今优秀的图片加载框架太多,例如: Volley ,Picasso,Imageloader,Glide等等。但是作为程序猿,懂得其中的实现原理还是相当重要的,只有懂得才能更好地使用。于是乎,今天我就简单设计一个网络加载图片框架。主要就是熟悉图片的网络加载机制。
一般来说,一个优秀的 图片加载框架(ImageLoader) 应该具备如下功能:
- 图片压缩
- 内存缓存
- 磁盘缓存
- 图片的同步加载
- 图片的异步加载
- 网络拉取
那我们就从以上几个方面进行介绍:
1.图片压缩(有效的降低OOM的发生概率)
图片压缩功能我在Bitmap 的高效加载中已经做了介绍这里不多说直接上代码。这里直接抽象一个类用于完成图片压缩功能。
2.内存缓存和磁盘缓存
缓存直接选择 LruCache 和 DiskLruCache 来完成内存缓存和磁盘缓存工作。
首先对其初始化:
创建完毕后,接下来则需要提供方法来视线添加以及获取的功能。首先来看内存缓存。
相对来说内存缓存比较简单,而磁盘缓存则复杂的多。磁盘缓存(LruDiskCache)并没有直接提供方法来实现,而是要通过Editor以及Snapshot 来实现对于文件系统的添加以及读取的操作。
首先看一下,Editor,它提供了commit 和 abort 方法来提交和撤销对文件系统的写操作。
Snapshot, 通过它可以获取磁盘缓存对象对应的 FileInputStream,但是FileInputStream 无法便捷的进行压缩,所以通过FileDescriptor 来加载压缩后的图片,最后将加载后的bitmap添加到内存缓存中。
3.同步加载
同步加载的方法需要外部在子线程中调用。
|
|
从方法中可以看出工作过程遵循如下几步:
首先尝试从内存缓存中读取图片,接着尝试从磁盘缓存中读取图片,最后才会从网络中拉取。此方法不能再主线程中执行,执行环境的检测是在loadBitmapFromHttp中实现的。
4.异步加载
|
|
从bindBitmap的实现来看,bindBitmap 方法会尝试从内存缓存中读取图片,如果读取成功就直接返回结果,否则会在线程池中去调用loadBitmap方法,当图片加载成功后再将图片、图片的地址以及需要绑定的imageView封装成一个LoaderResult对象,然后再通过mMainHandler向主线程发送一个消息,这样就可以在主线程中给imageView设置图片了。
下面来看一下,bindBitmap这个方法中用到的线程池和Handler,首先看一下线程池 THREAD_POOL_EXECUTOR 的实现。
1.使用线程池和handler的原因。
首先不能用普通线程去实现,如果采用普通线程去加载图片,随着列表的滑动可能会产生大量的线程,这样不利于效率的提升。 Handler 的实现 ,直接采用了 主线程的Looper来构造Handler 对象,这就使得 ImageLoader 可以在非主线程构造。另外为了解决由于View复用所导致的列表错位这一问题再给ImageView 设置图片之前会检查他的url有没有发生改变,如果发生改变就不再给它设置图片,这样就解决了列表错位问题。
|
|
总结:
图片加载的问题 ,尤其是大量图片的加载,对于android 开发者来说一直是比较困扰的问题。本文只是提到了最基础的一种解决方法,用于学习还是不错的。
最后结尾不多说,直接上demo:
自定义图片加载框架–运用MVP+Retrofit+Rxjava的应用架构