写点什么

Android 开发——优化 Glide 加载超大 gif 图

发布于: 2021 年 11 月 07 日

// If we couldn't decode the GIF, we will end up with a frame count of 0.


return null


}


//进行采样设置


val sampleSize = getSampleSize(header, width, height)


//创建解析器构建模式


val builder = GifDrawableBuilder()


builder.from(byteBuffer)


builder.sampleSize(sampleSize)


builder.isRenderingTriggeredOnDraw = true


// pl.droidsonroids.gif.GifOptions gifOptions = new pl.droidsonroids.gif.GifOptions();


// DES: 不含透明层可以加速渲染 但是透明的 gif 会渲染黑色背景


// gifOptions.setInIsOpaque();


val gifDrawable = builder.build()


val loopCount = gifDrawable.loopCount


if (loopCount <= 1) {


//循环一次的则矫正为无限循环


Log.v(TAG, "Decoded GIF LOOP COUNT WARN $loopCount")


gifDrawable.loopCount = 0


}


GifLibDrawableResource(gifDrawable, byteBuffer)


} catch (e: IOException) {


Log.v(TAG, "Decoded GIF Error" + e.message)


null


} finally {


Log.v(TAG, "Decoded GIF from stream in " + LogTime.getElapsedMillis(startTime))


}


}


}


序列化类:


class GifLibEncoder : ResourceEncoder<GifDrawable?> {


override fun getEncodeStrategy(options: Options): EncodeStrategy {


return EncodeStrategy.SOURCE


}


override fun encode(data: Resource<GifDrawable?>, file: File, options: Options): Boolean {


var success = false


if (data is GifLibDrawableResource) {


val byteBuffer = data.buffer


try {


ByteBufferUtil.toFile(byteBuffer, file)


success = true


} catch (e: IOException) {


e.printStackTrace()


}


// DES: 将 resource 编码成文件


Log.d(TAG, "GifLibEncoder -> {file.absolutePath}")


}


return success


}


}


注册组件,注解注册类继承 AppGlideModule 并在 registerComponents 中调用如下 fun:


@JvmStatic


fun registerGifLib(glide: Glide, registry: Registry) {


//优先使用 gifLib-Gif


val bufferDecoder = GifLibByteBufferDecoder(registry.imageHeaderParsers)


val gifLibTranscoder = GifLibBytesTranscoder()


val bitmapBytesTranscoder = BitmapBytesTranscoder()


val gifTranscoder = GifDrawableBytesTranscoder()


registry.prepend(


Registry.BUCKET_GIF, java.io.InputStream::class.java, GifDrawable::class.java,


GifLibDecoder(registry.imageHeaderParsers, bufferDecoder, glide.arrayPool)


).prepend(


Registry.BUCKET_GIF,


java.nio.ByteBuffer::class.java,


GifDrawable::class.java, bufferDecoder


).prepend(


GifDrawable::class.java, GifLibEncoder()


).register(


Drawable::class.java, ByteArray::class.java,


DrawableBytesTranscoder(


glide.bitmapPool,


bitmapBytesTranscoder,


gifTranscoder,


gifLibTranscoder


)


).register(


GifDrawable::class.java, ByteArray::class.java, gifLibTranscoder


)


}

Registry api 说明

  • append(..) 追加到最后,当内部的组件在 handles()返回 false 或失败时候使用追加组件

  • prepend(..)最佳到前面,当你的组件在失败时候使用原生提供组件

  • replace(..)替换组件


Result




验证组件是否注册成功


IGlideModule.with(view).load(url)


.placeholder(R.color.colorAccent)


.listener(object : RequestListener<Drawable> {


override fun onResourceReady(


resource: Drawable?, model: Any?,


target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean


): Boolean {


if (resource is pl.droidsonroids.gif.GifDrawable) {


Log.d("TAG", "giflib 的 Gifdrawable")


} else if (resource is com.bumptech.glide.load.resource.gif.GifDrawable) {


Log.d("TAG", "glide 的 Gifdrawable")


}


return false


}


override fun onLoadFailed(


e: GlideException?, model: Any?,


target: Target<Drawable>?, isFirstResource: Boolean


): Boolean = false


})


.into(view)


log: com.example.mydemo


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


D/TAG: giflib 的 Gifdrawable


缺陷




这样做看起来侵入性很低的替换了 Glide 的 gif 支持,并且还可以兼容 giflib 出错后使用原生组件,那么缺点呢?缺点也是非常头疼,通常我们会对一些图片加载需求做一些圆角或者圆形等等处理。glide 自己的 GifDrawable 支持的很好,几乎所有的 BitmapTransformation 都支持,而我们的缺失效了,究其原因是源码中所有 transform 设置最终调用到如下:


class BaseRequestOptions...


@NonNull


T transform(@NonNull Transformation<Bitmap> transformation, boolean isRequired) {


...省略


DrawableTransformation drawableTransformation =


new DrawableTransformation(transformation, isRequired);


transform(Bitmap.class, transformation, isRequired);


transform(Drawable.class, drawableTransformation, isRequired);


transform(BitmapDrawable.class, drawableTransformation.asBitmapDrawable(), isRequired);


//对 gifdrawble 的 Transformation 支持缘由


transform(GifDrawable.class, new GifDrawableTransformation(transformation), isRequired);


return selfOrThrowIfLocked();


}


}


由于源码已经固定了次转换注入口,除非我们自己修改源码编译或者 asm 手段。如何解决呢?先依旧照猫画虎GifLibDrawableTransformation然后实现


class GifLibDrawableTransformation(wrapped: Transformation<Bitmap>) : Transformation<GifDrawable> {


private val wrapped: Transformation<Bitmap> = Preconditions.checkNotNull(wrapped)


override fun transform(


context: Context, resource: Resource<GifDrawable?>, outWidth: Int, outHeight: Int


): Resource<GifDrawable?> {


val drawable = resource.get()


drawable.transform = object : Transform {


private val mDstRectF = RectF()


override fun onBoundsChange(rct: Rect) = mDstRectF.set(rct)


override fun onDraw(canvas: Canvas, paint: Paint, bitmap: Bitmap) {


val bitmapPool = Glide.get(context).bitmapPool


val bitmapResource: Resource<Bitmap> = BitmapResource(bitmap, bitmapPool)


val transformed = wrapped.transform(context, bitmapResource, outWidth, outHeight)


val transformedFrame = transformed.get()


canvas.drawBitmap(transformedFrame, null, mDstRectF, paint)


}


}


return resource


}


override fun equals(o: Any?): Boolean {


if (o is GifLibDrawableTransformation) {


return wrapped == o.wrapped


}


return false


}


override fun hashCode(): Int {


return wrapped.hashCode()


}


override fun updateDiskCacheKey(messageDigest: MessageDigest) {


wrapped.updateDiskCacheKey(messageDigest)


}


}


//每次调用 transform 时候注入下


val circleCrop = CircleCrop()


IGlideModule.with(this)


.load("http://tva2.sinaimg.cn/large/005CjUdnly1g6lwmq0fijg30rs0zu4qp.gif")


.transform(GifDrawable::class.java, GifLibDrawableTransformation(circleCrop))


.transform(circleCrop)


.into(iv_2)


缺陷攻克了?其实还没有完美解决,一来这样的书写方式不是很方便,二来目前对ScaleType.CENTER_CROP等支持还是有问题,如果你有更好的建议或者可以修复请提交 pr.


总结




glide已经非常优秀了,如果是仅仅少量使用 gif 完全可以胜任了,而且随着 android 版和硬件的升级,这些性能问题越来越少,但是如果你发现项目中因为 gif 的使用导致 oom 的问题较多可以尝试次优化,另外也可以降低手机发热耗电问题。生命不息折腾不止啊。


推荐:




从Glide源码解读到手写图片加载框架

评论

发布
暂无评论
Android开发——优化Glide加载超大gif图