我正在参与「启航计划」
前语
对于Glide
,信任各位同学都不生疏,作为最常用的图片加载框架,它广泛应用大大小小的项目傍边,一般咱们常规用法便是运用链式调用就能够给组件设置图片了,十分便捷高效,尽管已经在绝大数开发场景中够用了,但是在一些特定需求下,Glide
也供给了相应的延展性,便于大家应对各式各样的需求场景;
下文将介绍Glide
扩展的3种运用办法,希望对你有所协助
- 利用
Glide
整理缓存 -
Glide
默许装备设置 —AppGlideModule
- 完结
Glide
加载图片进展监听
利用Glide
整理缓存
一般咱们在运用Glide
的时分,在整理缓存有些很好的理由。
- 一个原因是调试,要保证
Glide
没有从内存或磁盘加载图画。 - 另一个原因时因为
Glide
的缓存机制,在加载越来越多图片的时分,磁盘空间会占用越来越多,所以需求允许用户铲除一些磁盘空间。
对此,Glide
供给了两种办法可用于整理缓存,如下代码所示
lifecycleScope.launchWhenCreated {
//第一种办法
withContext(Dispatchers.IO) {
Glide.get(this@MainActivity).clearDiskCache()
}
//第二种办法
withContext(Dispatchers.Main) {
Glide.get(this@MainActivity).clearMemory()
}
}
-
clearDiskCache
只能在后台进行调用,所以咱们在子线程调用此办法 -
clearMemory
只能在主线程进行调用
Glide默许装备设置 — AppGlideModule
在运用Glide
链式调用的时分,它会默许运用自带的缓存目录,缓存大小,磁盘缓存战略等,但是在一些场景中,因为缓存大小限制,指定缓存途径,或许多个当地等等,那么就需求自己界说这些装备,所以就要用到AppGlideModule
;
-
首要咱们需求创立一个自界说
AppGlideModule
让它承继AppGlideModule
类,完结它的空办法class MyAppGlide : AppGlideModule() { //控制是否从Manifest中文件装备解析 override fun isManifestParsingEnabled(): Boolean { return false } override fun applyOptions(context: Context, builder: GlideBuilder) { super.applyOptions(context, builder) } override fun registerComponents(context: Context, glide: Glide, registry: Registry) { } }
-
经过
applyOptions
办法传入了GlideBuilder
实例,它本身便是单例的,能够在这里去修正它里边的那些装备项,不再运用默许值-
可设置bitmap复用缓存池,运用自带的
LruBitmapPool
可设置当时缓冲池容量最大值,也能够自界说缓存池//设置bitmap目标复用缓存池 builder.setBitmapPool(LruBitmapPool(1024 * 1024 * 32)
-
可设置内存缓存,运用自带的
LruResourceCache
可设置最大容量,也能够自界说//设置内存缓存 builder.setMemoryCache(LruResourceCache(1024 * 1024 * 32))
-
可装备当时磁盘缓存目录和最大缓存
builder.setDiskCache( //装备磁盘缓存目录和最大缓存 DiskLruCacheFactory( (context.externalCacheDir ?: context.cacheDir).absolutePath, "imageCache", 1024 * 1024 * 50 ) )
-
可装备相关图片加载选项,在这里设置
placeHolder
加载图,error
过错图等等,一般来说这些选项都能够进行一致运用的,这样就不必每次都自己单独设置,会显得有点冗余;都能够在AppGlideModule
中进行装备builder.setDefaultRequestOptions { return@setDefaultRequestOptions RequestOptions() .placeholder(android.R.drawable.ic_menu_upload_you_tube) .error(android.R.drawable.ic_menu_call) .centerCrop() .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) .format(DecodeFormat.DEFAULT) .encodeQuality(90) }
-
-
最终需求加上
@GlideModule
注解,这样在编译阶段 Glide 就能够经过 APT 解析到咱们的这一个完结类,然后将咱们的装备参数设置为默许值
值得注意的事项
- 假如运用
AppGlideModule
,要避免被混杂,需求在proguard-rules.pro
参加相应规则
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class * extends com.bumptech.glide.module.AppGlideModule {
<init>(...);
- 而且在一个应用中只允许存在一个
AppGlideModule
,创立多个会直接报错
完结Glide
加载图片进展监听
有时分在加载一些过大的图和网络速度偏慢的时分,加载图画往往都需求几秒钟,乃至更久,为了增加用户体会,咱们往往会设置loading状态或许显示加载进展条;前者比较简略,后者尽管增加加载进展条并不难,但很不幸的是,Glide
并没有揭露下载进展监听器。为了监听 Glide 的下载进展,你需求重写 Glide
模块并供给你自己的OkHttp
集成
增加Glide
注解和 OkHttp
集成
直接在build.gradle
文件中参加相应依靠
kapt 'com.github.bumptech.glide:compiler:4.12.0'
implementation("com.github.bumptech.glide:okhttp3-integration:4.11.0") {
exclude group: 'glide-parent'
}
这里需求注意的是,要在顶部参加相应插件,而且不要运用annotationProcessor
,这样新界说的模块将不会运用,也不会报任何过错,假如你运用的是Kotlin
编写,那就用kapt
,它是Kotlin
的注释处理器;
plugins {id 'kotlin-kapt'}
创立监听接口
-
首要要创立
ResponseProgressListener
接口,是用来监听当时url下载进展进行更新操作//通知正在监听该url下载进展的操作 interface ResponseProgressListener { fun update(url: HttpUrl, byteRead: Long, contentLength: Long) }
-
创立
OnProgressBarListener
接口,获取当时进展而且改写UI
中的ProgressBar
,能够获取当时进展百分比(0-100%)//监听progressbar进展 interface OnProgressBarListener { val currentPercentage:Float fun onProgress(bytesRead: Long, expectedLength: Long) }
创立DispatchingProgressManager
主要用来盯梢一切图片URL
的进展和通知UI
监听器更新主线程中的进展条状态
class DispatchingProgressManager internal constructor() : ResponseProgressListener {
companion object {
//显示当时url的进展
private val PROGRESSES = HashMap<String?, Long>()
//存储UI监听器
private val LISTENERS = HashMap<String?, OnProgressBarListener>()
//将url和进展监听器存入到hashmap中
internal fun expect(url: String?, listener: OnProgressBarListener) {
LISTENERS[url] = listener
}
//假如下载完结/下载失利,移除
internal fun forget(url: String?) {
LISTENERS.remove(url)
PROGRESSES.remove(url)
}
}
//后台线程通知进展,ui线程处理UI
private val handler: Handler = Handler(Looper.getMainLooper())
override fun update(url: HttpUrl, byteRead: Long, contentLength: Long) {
val key = url.toString()
//假如没有进展监听器,直接显示照片
val listener = LISTENERS[key] ?: return
if (contentLength <= byteRead) {
forget(key)
}
if (needsDispatch(key, byteRead, contentLength, listener.currentPercentage)) { //8
Log.d("glide", "update: $contentLength")
handler.post { listener.onProgress(byteRead, contentLength) }
}
}
private fun needsDispatch(key: String, current: Long, total: Long, granularity: Float): Boolean {
if (granularity == 0f || current == 0L || total == current) {
return true
}
val percent = 100f * current / total
val currentProgress = (percent / granularity).toLong()
val lastProgress = PROGRESSES[key]
return if (lastProgress == null || currentProgress != lastProgress) {
PROGRESSES[key] = currentProgress
true
} else {
false
}
}
}
简略来说,该类主要便是创立两个HashMap
分别寄存当时图片加载进展和UI监听器Listener
,一起在needsDispatch
办法中核算当时进展百分比,存入到界说好的HashMap
中,然后经过handler
更新UI
组件;对外供给来个办法expect
和forget
,前者将URL
及其UI 监听器
增加到HashMap
,在下载开始时调用;或许在下载完结/下载失利的时分调用,移除当时界说的URL
OkHttp
的 ResponseBody
中监听进展
在内部根据 contentLength
和已读取到的流字节数来,然后将它们的值传入到咱们已经界说好的progressListener
中
class ProgressResponseBody internal constructor(
private val url: HttpUrl,
private val responseBody: ResponseBody,
private val progressListener: ResponseProgressListener
) : ResponseBody() {
private var bufferedSource: BufferedSource? = null
override fun contentType(): MediaType? {
return responseBody.contentType()
}
override fun contentLength(): Long {
return responseBody.contentLength()
}
override fun source(): BufferedSource {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()))
}
return this.bufferedSource!!
}
private fun source(source: Source): Source {
return object : ForwardingSource(source) {
var totalBytesRead = 0L
@Throws(IOException::class)
override fun read(sink: Buffer, byteCount: Long): Long {
val bytesRead = super.read(sink, byteCount)
val fullLength = responseBody.contentLength()
if (bytesRead.toInt() == -1) { // this source is exhausted
totalBytesRead = fullLength
} else {
totalBytesRead += bytesRead
}
progressListener.update(url, totalBytesRead, fullLength)
return bytesRead
}
}
}
}
自界说的AppGlideModule
中扩展
接着需求在之前界说MyAppGlide
中重写registerComponents
办法,这里运用咱们自己的OkHttpClient
,增加相应的拦截器Interceptor
,并将之前已经界说好的DispatchingProgressManager
传入进来;一起,供给了一个OkHttpUrlLoader.Factory
类方便替换Glide
的默许网络库
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
val client = OkHttpClient.Builder()
.addNetworkInterceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
val listener = DispatchingProgressManager()
response.newBuilder()
.body(ProgressResponseBody(request.url(), response.body()!!, listener))
.build()
}
.build()
glide.registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client))
}
封装GlideImageLoader
加载图片
最终在前面一切准备工作都完结了,咱们需求将当时显示的ImageView
和ProgressBar
组件拿到,对外供给一个load
办法加载图片,在该办法中调用expected
办法存储进展和初始化侦听器,一起更新ProgressBar
的进展,在下载开始的时分让它可见,下载完毕后躲藏
//加载图片
fun load(url: String?, options: RequestOptions?) {
if (options == null) return
onConnecting()
DispatchingProgressManager.expect(url, object : OnProgressBarListener { //4
override val currentPercentage: Float //5
get() = 1.0f
override fun onProgress(bytesRead: Long, expectedLength: Long) {
if (mProgressBar != null) {
Log.d("glide", "onProgress: ${(100 * bytesRead / expectedLength).toInt()}")
mProgressBar.progress = (100 * bytesRead / expectedLength).toInt() //6
}
}
})
Glide.with(mImageView!!.context)
.load(url)
.apply(options)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
DispatchingProgressManager.forget(url)
onFinished()
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
DispatchingProgressManager.forget(url)
onFinished()
return false
}
})
.into(mImageView)
}
在上述代码中,运用了
Glide
揭露的资源监听器,能够图片加载失利/成功的时分执行相应的操作,这里无论下载失利还是下载成功,都将图片URL
移除,一起躲藏ProgressBar
自此,咱们就完结了利用
Glide
完结了图片加载进展监听