下文将介绍Glide扩展的3种使用方式,希望对你有所帮助:
1、利用Glide清理缓存
2、Glide默认配置设置 — AppGlideModule
3、实现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;
首先我们需要创建一个自定义OkHttpGlideModule 让它继承AppGlideModule类,实现它的空方法:
@GlideModule
class OkHttpGlideModule : AppGlideModule() {
override fun isManifestParsingEnabled(): Boolean {
return false
}
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
val trustAllCerts =arrayOf(object : X509TrustManager {
override fun checkClientTrusted(
chain: Array?, authType: String?
) {
}
override fun checkServerTrusted(
chain: Array?, authType: String?
) {
}
override fun getAcceptedIssuers(): Array? {
return arrayOf()
}
}
)
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
val sslSocketFactory = sslContext.socketFactory
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(sslSocketFactory,
TrustAllCertsTrustManager()
)
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()
}
builder.hostnameVerifier{ _, _-> true }
registry.replace(
GlideUrl::class.java,
InputStream::class.java,
OkHttpUrlLoader.Factory(builder.build())
)
}
}
通过applyOptions方法传入了GlideBuilder实例,它本身就是单例的,可以在这里去修改它里面的那些配置项,不再使用默认值。
可设置bitmap复用缓存池,使用自带的LruBitmapPool可设置当前缓冲池容量最大值,也可以自定义缓存池。
//设置bitmap对象复用缓存池
builder.setBitmapPool(LruBitmapPool(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 的默认网络库。
封装GlideProgressUtil加载图片
最后在前面所有准备工作都完成了,我们需要将当前显示的ImageView和ProgressBar组件拿到,对外提供一个load方法加载图片,在该方法中调用expected方法存储进度和初始化侦听器,同时更新ProgressBar的进度,在下载开始的时候让它可见,下载结束后隐藏。
/**
*
* @description
* @author as752497576@gmail.com
* @time 2023/3/12
*/
class GlideProgressUtil {
//加载图片
fun loadTest( ) {
val apkUrl =
"https://meeting.onlineinline.com/api/ossUpgrade/A/A/1.3.3/app-release-sign-8c979ad0d.apk"
DispatchingProgressManager.expect(apkUrl, object : OnProgressBarListener { //4
override val currentPercentage: Float //5
get() = 1.0f
override fun onProgress(bytesRead: Long, expectedLength: Long) {
AppLog.getInstance().e(" onProgress: ${(100 * bytesRead / expectedLength).toInt()} ")
AppLog.getInstance().e(" onProgress:=== ${(100 * bytesRead / expectedLength).toInt()} ")
}
})
Glide.with(BaseApplication.mContext)
.downloadOnly()
.load(apkUrl)
.listener(object : RequestListener<File> {
override fun onLoadFailed(
e: GlideException?,
model: Any,
target: Target<File>,
isFirstResource: Boolean
): Boolean {
AppLog.getInstance().e("第三方的速度 下载失败onResourceReady: $e")
DispatchingProgressManager.forget(apkUrl)
return false
}
override fun onResourceReady(
resource: File,
model: Any,
target: Target<File>,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
// Toast.makeText(getContext(), "下载成功", Toast.LENGTH_SHORT).show();
DispatchingProgressManager.forget(apkUrl)
AppLog.getInstance().e("第三方的速度onResourceReady: $resource")
AppLog.getInstance().e("第三方的速度model: $model")
AppLog.getInstance().e("第三方的速度model: $target")
AppLog.getInstance().e("第三方的速度dataSource: $dataSource")
AppLog.getInstance().e("第三方的速度onResourceReady: $isFirstResource")
return false
}
})
.preload()
}
}
SSL证书绕过
在上述代码中,使用了Glide公开的资源监听器,可以图片加载失败/成功的时候执行相应的操作,这里无论下载失败还是下载成功,都将图片URL移除,同时隐藏ProgressBar。
自此,我们就完成了利用Glide实现了图片加载进度监听。