glide4.11.0封装gfilib优化gif图片加载


需求: 目前项目中加载进度框采用的是一个gif图片, 使用图片加载框架为glide.

Glide 是如何加载 GIF 动图的?

首先需要区分加载的图片类型,即网络请求拿到输入流后,获取输入流的前三个字节,若为 GIF 文件头,则返回图片类型为 GIF。

确认为 GIF 动图后,会构建一个 GIF 的解码器(StandardGifDecoder),它可以从 GIF 动图中读取每一帧的数据并转换成 Bitmap,然后使用 Canvas 将 Bitmap 绘制到 ImageView 上,下一帧则利用 Handler 发送一个延迟消息实现连续播放,所有 Bitmap 绘制完成后又会重新循环,所以就实现了加载 GIF 动图的效果。

未使用gifli加载gif图,使用glide加载gif效果:

  • 内存消耗: 80.7

使用giflib加载gif图效果:

  • 内存消耗: 40.9

解决方案 : 使用 giflib 库 (gif编解码库c++ ),并提供Java 。使用它要比glide加载GIF效果效果要好,glide加载加载GIF图片CPU占用高,并且内存占用一直在增加。采用JNI调用的方式加载gif图.

具体步骤 :

下载giflib 和 framesequence

  1. giflib 下载 framesequence其jin依赖于giflib,所以,得下载其源码,地址在 https://android.googlesource.com/platform/external/giflib/+/refs/heads/master 可以看到giflib已经集成到Android源码里面了

或者在这里下载 https://android.googlesource.com/platform/external/giflib/+/android-9.0.0_r16

  1. framesequence 下载 framesequence是Androidframework中ex里的一个工具包,其源码地址在
    https://android.googlesource.com/platform/frameworks/ex/+/android-9.0.0_r16/framesequence/

导入并集成 giflib 和 framesequence

首先把项目转化为c++项目. 然后在cpp目录创建giflib文件夹,将下载的giflib源码拷贝到giflib文件夹下面

如下图:

将无用的文件删除后在使用cmakelist编译

cmake_minimum_required(VERSION 3.4.1)

#引入源码
file(GLOB_RECURSE GIF_LIB ${CMAKE_SOURCE_DIR}/giflib/*.*)
file(GLOB_RECURSE FRAME_SEQUENCE ${CMAKE_SOURCE_DIR}/*.cpp*)

add_library(mygif
        SHARED
        ${FRAME_SEQUENCE}
        ${GIF_LIB})

list(APPEND LIBS
        jnigraphics
        android
        GLESv2
        log
        )

set(LIBS)
list(APPEND LIBS
        jnigraphics
        android
        GLESv2
        log
        )

target_link_libraries(mygif ${LIBS})

glide的配置gif使用giflib集成

GifDecoder

package com.example.giflibdemo;

import android.graphics.Bitmap;
import android.support.rastermill.FrameSequence;
import android.support.rastermill.FrameSequenceDrawable;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;

import java.io.IOException;
import java.io.InputStream;

public class GifDecoder implements ResourceDecoder<InputStream, FrameSequenceDrawable> {

    private BitmapPool bitmapPool;

    public GifDecoder(BitmapPool bitmapPool) {
        this.bitmapPool = bitmapPool;
    }

    @Override
    public boolean handles(@NonNull InputStream source, @NonNull Options options) throws IOException {
        return true;
    }

    @Nullable
    @Override
    public Resource<FrameSequenceDrawable> decode(@NonNull InputStream source, int width, final int height, @NonNull Options options) throws IOException {
        FrameSequence frameSequence = FrameSequence.decodeStream(source);
        FrameSequenceDrawable frameSequenceDrawable = new FrameSequenceDrawable(frameSequence, new FrameSequenceDrawable.BitmapProvider() {
            @Override
            public Bitmap acquireBitmap(int minWidth, int minHeight) {
                return bitmapPool.get(minWidth, minHeight, Bitmap.Config.ARGB_8888);
            }

            @Override
            public void releaseBitmap(Bitmap bitmap) {
                bitmapPool.put(bitmap);
            }
        });
        return new GifResource(frameSequenceDrawable);
    }
}

GifGlideModule

package com.example.giflibdemo;

import android.content.Context;
import android.support.rastermill.FrameSequenceDrawable;

import androidx.annotation.NonNull;

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.module.LibraryGlideModule;

import java.io.InputStream;

@GlideModule
public class GifGlideModule extends AppGlideModule {
//public class GifGlideModule extends LibraryGlideModule {


    @Override
    public void applyOptions(@NonNull  Context context, @NonNull  GlideBuilder builder) {
        super.applyOptions(context, builder);
    }

    @Override
    public void registerComponents(@NonNull Context context,
                                   @NonNull Glide glide, @NonNull Registry registry) {
        super.registerComponents(context, glide, registry);
        registry.append(Registry.BUCKET_GIF, InputStream.class,
                FrameSequenceDrawable.class, new GifDecoder(glide.getBitmapPool()));
//        registry.append(Registry.BUCKET_GIF, InputStream.class,
//                FrameSequenceDrawable.class, new GifDecoder(glide.getBitmapPool()));
    }

    @Override
    public boolean isManifestParsingEnabled() {
        return false;
    }
}

GifResource

package com.example.giflibdemo;

import android.support.rastermill.FrameSequenceDrawable;

import androidx.annotation.NonNull;

import com.bumptech.glide.load.resource.drawable.DrawableResource;


public class GifResource extends DrawableResource<FrameSequenceDrawable> {

    public GifResource(FrameSequenceDrawable drawable) {
        super(drawable);
    }

    @NonNull
    @Override
    public Class<FrameSequenceDrawable> getResourceClass() {
        return FrameSequenceDrawable.class;
    }

    @Override
    public int getSize() {
        return 0;
    }

    @Override
    public void recycle() {
        drawable.stop();
        drawable.destroy();
    }
}

GlideExtension

package com.example.giflibdemo;

import android.support.rastermill.FrameSequenceDrawable;

import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.annotation.GlideType;
import com.bumptech.glide.request.RequestOptions;

@com.bumptech.glide.annotation.GlideExtension
public class GlideExtension {

    private GlideExtension() {

    }

    final static RequestOptions DECODE_TYPE = RequestOptions
            .decodeTypeOf(FrameSequenceDrawable.class)
            .lock();

    @GlideType(FrameSequenceDrawable.class)
    public static RequestBuilder<android.support.rastermill.FrameSequenceDrawable> asGif2(RequestBuilder<FrameSequenceDrawable> requestBuilder) {
//        RequestBuilder<android.support.rastermill.FrameSequenceDrawable
        return requestBuilder.apply(DECODE_TYPE);

    }

}

开始使用


        int gif = R.mipmap.loading_gif;
        //String gif = "http://2zhoumu-comic-public-test.oss-cn-hangzhou.aliyuncs.com/cover/comic/gc1100004.gif";
        //使用giflib加载
        GlideApp.with(this).as(FrameSequenceDrawable.class).load(gif).into(imageView);
        //使用glide原生加载
//        Glide.with(this).asGif().load(gif).into(imageView);

加载思路

考虑如何使用,一般是给ImageView设置bitmap,但是bitmap存储效率低.故而使用Drawable. ImageView有一个setImageDrawable的方法

思路:

  1. 先将gif解压成为一帧一帧的数据
  2. 将数据绘制到drawable上面去
  3. 最后将drawable设置到ImagView上面去

系统有个BitmapDrawable,但是这里需要自己定义一个GifDrawable用于显示gif图片 这里framesequence 已经帮我们做了.

创建自定义GifDrawable

GifDrawable 需要做的操作:

  1. 首先Java准备一个Bitmap Bitmap是要Java端初始化出来的 传给C++
  2. gif 解压出来之后每一帧(Screen) 填充 成Bitmap
  3. 把Bitmap 绘制到Canvas 利用 canvas.drawBitmap

注意点:
需要让jni创建java对象


glide 使用glidemodule 官方文档

https://bumptech.github.io/glide/doc/configuration.html


其他资料

Bitmap - 称作位图,一般位图的文件格式后缀为bmp,当然编码器也有很多如RGB565、RGB888。作为一种逐像素的显示对象执行效率高,但是缺点也很明显存储效率低。我们理解为一种

存储对象比较好。
Drawable - 作为Android平下通用的图形对象,它可以装载常用格式的图像,比如GIF、PNG、JPG,当然也支持BMP,当然还提供一些高级的可视化对象,比如渐变、图形等。

giflib api 加载方式:

  1. 一次性读取gif的信息,然后加载
  2. 使用流的方式读取,自己去解析加载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值