Gif 加载对比
用 android-gif-drawable 来加载gif 和glide 对比一下
注意这里的GifDrawable 和 glide里面的不是一个
Thread() {
run {
var futureTarget: FutureTarget<File?>? = Glide.with(this)
.downloadOnly()
.load(url)
.submit() // 一次性加载了 整个gif 图片的大小
val drawable = GifDrawable(futureTarget?.get()!!.path)
runOnUiThread {
imageview.setImageDrawable(drawable)
}
}
}.start()
加载gif 时的性能对比可以看出来整体加载曲线很平稳,且内存占用只有62.8mb
可以看下具体的内存占用分布
再来看看原生glide 加载 gif的 场景:
可以看出来 使用glide 原生加载gif的时候 峰值内存达到175mb
平稳以后也有 接近115mb的内存占用
加载流程
可以看出来这个Drawable的构造方式有很多
我们一般都是传一个固定file path进去,
可以看出来 在构建Drawable的过程中,会调用一个c++方法 去打开这个文件
这个jni 方法 主要就是 返回一个GifInfo对象的指针地址
在我们对imageview 调用了setImageViewDrawable方法以后, 其实会有一系列的回调到我们的drawable中
其中最重要的就是draw 和invalidateself 2个方法
扯远了, 我们回到之前的 GifDrawble 构造方法中来
doWork 我们最关注的 就是 红框中的2个步骤
第一个就是renderFrame
最后调用的是这个jni方法,参数其实就是一个空的bitmap
这个jni方法做的就是把这个bitmap的像素值 按照gif里的帧 给她填满
所以看到这 大家应该有一个感受了, 如果你是在主线程中创建的GifDrawable 那么至少会有两次 耗时操作
一个是读取文件,另外一个就是这里的绘制bitmap中的像素,
所以我们在用这个gif库的时候 条件允许 可以在子线程去创建GifDrawable
逐帧播放的逻辑
前文我们跟到了手动调用doWork的逻辑,在GifDrawable 构造函数 中,doWork 除了 解析出 gif的第一帧以外,还发送了一个handler 消息 我们可以跟一下这个handler 消息做了啥
可以看出来 最终还是 将renderTask 放到 一个线程池中去执行了,也就是说 对于这个Gif库来说,从Gif的第二帧 开始 他都是100% 在子线程在做的 帧解析
另外我们也可以看到 相比于Glide,这个库的优点,这个库甚至都没有使用BitmapPool,全程就只有构造函数中 createBitmap 一处调用,所谓的逐帧播放 都是对这个bitmap 内存的反复擦写罢了。
相比于Glide,不但 少了一个GIF本身的文件内存, 甚至 bitmap 对象池都省略了。