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

Posted 六道对穿肠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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. 使用流的方式读取,自己去解析加载

以上是关于glide4.11.0封装gfilib优化gif图片加载的主要内容,如果未能解决你的问题,请参考以下文章

GIF图怎么旋转

腾讯总裁刘炽平回应组织优化传闻;美国软件工程师平均薪水最高;GIF动图发明人去世|极客头条

琐碎的JS性能优化

Imagick PHP Gif 优化中的模糊等效项是啥?

电脑软件:最好用的Gif动图制作工具——ScreenToGif

用Java实现抖音等各种小视频批量转换为gif动态图