package com.base.common.glide
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.model.*
import com.bumptech.glide.load.model.ModelLoader.LoadData
import java.io.InputStream
import okhttp3.OkHttpClient
class OkHttpGlideUrlLoader : ModelLoaderFactory<GlideUrl, InputStream>,
ModelLoader<GlideUrl, InputStream> {
private val client: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(ProgressInterceptor())
.build()
override fun buildLoadData(
model: GlideUrl,
width: Int,
height: Int,
options: Options
): LoadData<InputStream>? {
return LoadData(model, OkHttpStreamFetcher(client, model))
}
override fun handles(model: GlideUrl): Boolean {
return true
}
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<GlideUrl, InputStream> {
return OkHttpGlideUrlLoader()
}
override fun teardown() {
}
}
package com.base.common.glide
import android.content.Context
import com.bumptech.glide.Glide
import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.Registry
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.module.AppGlideModule
import com.bumptech.glide.load.model.GlideUrl
import java.io.InputStream
import okhttp3.OkHttpClient
@GlideModule
class OkHttpLibraryGlideModule : AppGlideModule() {
override fun registerComponents(
context: Context, glide: Glide, registry: Registry
) {
registry.replace(GlideUrl::class.java, InputStream::class.java,OkHttpGlideUrlLoader())
}
override fun applyOptions(context: Context, builder: GlideBuilder) {
}
}
```kotlin
package com.base.common.glide
import android.os.Build
import android.util.Log
import com.bumptech.glide.Priority
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.HttpException
import com.bumptech.glide.load.data.DataFetcher
import okhttp3.Call
import okhttp3.Response
import java.io.IOException
import java.io.InputStream
import okhttp3.ResponseBody
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.util.Synthetic
import okhttp3.Request
import java.lang.ClassCastException
import com.bumptech.glide.util.ContentLengthInputStream
import com.bumptech.glide.util.Preconditions
class OkHttpStreamFetcher(client: Call.Factory, url: GlideUrl) : DataFetcher<InputStream>,
okhttp3.Callback {
companion object{
private const val TAG = "OkHttpFetcher"
}
private val client: Call.Factory? = client
private val url: GlideUrl? = url
@Synthetic
var stream: InputStream? = null
@Synthetic
var responseBody: ResponseBody? = null
private var call: Call? = null
private var callback: DataFetcher.DataCallback<in InputStream>? = null
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
val requestBuilder: Request.Builder = Request.Builder().url(url!!.toStringUrl())
for ((key, value) in url.headers) {
requestBuilder.addHeader(key, value)
}
val request: Request = requestBuilder.build()
this.callback = callback
call = client?.newCall(request)
if (Build.VERSION.SDK_INT !== Build.VERSION_CODES.O) {
call!!.enqueue(this)
} else {
try {
// Calling execute instead of enqueue is a workaround for #2355, where okhttp throws a
// ClassCastException on O.
onResponse(call!!, call!!.execute())
} catch (e: IOException) {
onFailure(call!!, e)
} catch (e: ClassCastException) {
// It's not clear that this catch is necessary, the error may only occur even on O if
// enqueue is used.
onFailure(call!!, IOException("Workaround for framework bug on O", e))
}
}
}
override fun cleanup() {
try {
if (stream != null) {
stream!!.close()
}
} catch (e: IOException) {
// Ignored
}
if (responseBody != null) {
responseBody!!.close()
}
callback = null
}
override fun cancel() {
try {
if (stream != null) {
stream!!.close()
}
} catch (e: IOException) {
// Ignored
}
if (responseBody != null) {
responseBody!!.close()
}
callback = null
}
override fun getDataClass(): Class<InputStream> {
return InputStream::class.java
}
override fun getDataSource(): DataSource {
return DataSource.REMOTE
}
override fun onFailure(call: Call, e: IOException) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "OkHttp failed to obtain result", e);
}
callback!!.onLoadFailed(e);
}
override fun onResponse(call: Call, response: Response) {
responseBody = response.body()
if (response.isSuccessful) {
val contentLength: Long = Preconditions.checkNotNull(responseBody).contentLength()
stream = ContentLengthInputStream.obtain(responseBody!!.byteStream(), contentLength)
callback!!.onDataReady(stream)
} else {
callback!!.onLoadFailed(HttpException(response.message(), response.code()))
}
}
}
```kotlin
package com.base.common.glide
import okhttp3.ResponseBody
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
open class ProgressInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
val response: Response = chain.proceed(request)
val url: String = request.url().toString()
val body: ResponseBody? = response.body()
return response.newBuilder().body(ProgressResponseBody(url, body!!)).build()
}
companion object {
val LISTENER_MAP: MutableMap<String, ProgressListener> = HashMap()
//入注册下载监听
fun addListener(url: String, listener: ProgressListener) {
LISTENER_MAP[url] = listener
}
//取消注册下载监听
fun removeListener(url: String) {
LISTENER_MAP.remove(url)
}
}
}
package com.base.common.glide
interface ProgressListener {
fun onProgress(progress: Int)
}
package com.base.common.glide
import androidx.annotation.Nullable
import okhttp3.MediaType
import okhttp3.ResponseBody
import okio.*
class ProgressResponseBody(url: String?, private val responseBody: ResponseBody) :
ResponseBody() {
private var bufferedSource: BufferedSource? = null
private var listener: ProgressListener? = ProgressInterceptor.LISTENER_MAP[url]
@Nullable
override fun contentType(): MediaType? {
return responseBody.contentType()
}
override fun contentLength(): Long {
return responseBody.contentLength()
}
override fun source(): BufferedSource {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(ProgressSource(responseBody.source()))
}
return bufferedSource!!
}
private inner class ProgressSource internal constructor(source: Source?) :
ForwardingSource(source) {
var totalBytesRead: Long = 0
var currentProgress = 0
override fun read(sink: Buffer, byteCount: Long): Long {
val bytesRead = super.read(sink, byteCount)
val fullLength = responseBody.contentLength()
if (bytesRead == -1L) {
totalBytesRead = fullLength
} else {
totalBytesRead += bytesRead
}
val progress = (100f * totalBytesRead / fullLength).toInt()
if (listener != null && progress != currentProgress) {
listener!!.onProgress(progress)
}
if (listener != null && totalBytesRead == fullLength) {
listener = null
}
currentProgress = progress
return bytesRead
}
}
companion object {
private const val TAG = "XGlide"
}
}
<meta-data android:name="om.base.common.OkHttpLibraryGlideModule"
android:value="AppGlideModule"/>
plugins {
id 'kotlin-kapt'
}
api "com.github.bumptech.glide:glide:4.11.0"
kapt 'com.github.bumptech.glide:compiler:4.11.0'
GlideApp.with(this)
.asDrawable()
.load("https://img1.baidu.com/it/u=3802863026,3009062800&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500")
.diskCacheStrategy(DiskCacheStrategy.NONE)
// 设置缓存之后只会有一次进度,所以这里去除缓存
.skipMemoryCache(true)
.into(object: CustomTarget<Drawable?>(){
override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable?>?
) {
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
原文连接:https://www.jianshu.com/p/61a0761e6e81