android app动图优化:源码giflib加载gif动图,性能秒杀glide

1、看博客之前,请先了解gif动图编码,自行百度;我的理解是,gif可以看作一系列帧动画,我们加载动图时只要每一帧绘制出来就可以形成gif动图了;

gif大小1.93M

加载之前,内存使用14.95M,空闲8.36M:

android app动图优化:源码giflib加载gif动图,性能秒杀glide_第1张图片

glide加载gif,使用21.09M,加载2Mgif使用了6M;

android app动图优化:源码giflib加载gif动图,性能秒杀glide_第2张图片

giflib源码加载,使用15.27M,加载2Mgif使用了0.32M;

android app动图优化:源码giflib加载gif动图,性能秒杀glide_第3张图片

giflib内存节省比glide强悍20倍

2、首先你要下载源码,拷贝giflib相关文件,文件路径:

 \android-6.0.0_r1\external\giflib

这里你只需要拷贝我们需要的文件,

android app动图优化:源码giflib加载gif动图,性能秒杀glide_第4张图片

拷贝到我们的项目目录cpp下:

android app动图优化:源码giflib加载gif动图,性能秒杀glide_第5张图片

这里文件跟我的不一样,不要紧,我进行了重新命名,待会儿用不到的c文件可以直接删除。

上代码了,代码里都有注释。


/**
 * 调用android 源码展示gif
 * Q164454216
 * Created by xiaoke on 2017/4/27.
 */

public class GifHandler {
    //代表C层的地址;
    private long gifPoint;

    public long getGifPoint() {
        return gifPoint;
    }

    public GifHandler(long gifPoint) {
        this.gifPoint = gifPoint;
    }

    static {
        try {
            System.loadLibrary("native-lib");
        } catch (Exception e) {
            Log.e(TAG, "static initializer: "+e.toString());


        }
    }

    //    public static native int getWidth(gifPoint);
    public static native long loadGif(byte[] path);

    public static native int getWidth(long gifHandler);

    public static native int getHeight(long gifHandler);

    public static native int getNextTime(long gifHandler);

    public static native int updateFrame(long gifHandler, Bitmap bitmap);

    public static GifHandler load(String path) {
        long gifHander = loadGif(path.getBytes());
        GifHandler gifHandler = new GifHandler(gifHander);
        return gifHandler;
    }
}

这里我们建了一个加载gif工具类,相关native方法在这里进行执行。

下面native-lib.cpp相关代码

extern "C" {
/**
 * 调用c展示gif动态图片
 */
JNIEXPORT jint JNICALL
Java_com_example_xiaoke_gifplayer_GifHandler_getWidth(JNIEnv *env, jclass type, jlong gifHandler) {
    GifFileType *gifFileType = (GifFileType *) gifHandler;
    return gifFileType->SWidth;

}
JNIEXPORT jint JNICALL
Java_com_example_xiaoke_gifplayer_GifHandler_getHeight(JNIEnv *env, jclass type, jlong gifHandler) {

    GifFileType *gifFileType = (GifFileType *) gifHandler;
    return gifFileType->SHeight;

}

JNIEXPORT jint JNICALL
Java_com_example_xiaoke_gifplayer_GifHandler_getNextTime(JNIEnv *env, jclass type,
                                                         jlong gifHandler) {
    GifFileType *gifFileType = (GifFileType *) gifHandler;
    GifBean *gifBean = (GifBean *) gifFileType->UserData;
    return gifBean->frame_duration;
}

JNIEXPORT jint  JNICALL
Java_com_example_xiaoke_gifplayer_GifHandler_updateFrame(JNIEnv *env, jclass type, jlong gifHandler,
                                                         jobject bitmap) {
    //强转代表gif的结构体
    GifFileType *gifFileType = (GifFileType *) gifHandler;
    GifBean *gifBean = (GifBean *) gifFileType->UserData;
    AndroidBitmapInfo info;
    //代表一副图片的像素数组
    void *pixels;
    //info赋值
    AndroidBitmap_getInfo(env, bitmap, &info);
    //锁定bitmap,一副图片是二维数组
    AndroidBitmap_lockPixels(env, bitmap, &pixels);
    //绘制一帧图片
    drawFrame(gifFileType, &info, (int *) pixels, gifBean->current_frame, false);
    //当前帧+1;
    gifBean->current_frame += 1;
    //当绘制到最后一帧
    if (gifBean->current_frame >= gifBean->total_frame) {
        gifBean->current_frame = 0;
    }
    AndroidBitmap_unlockPixels(env, bitmap);
    return gifBean->frame_duration;
}

//jstring to char*
char* jstringTostring(JNIEnv* env, jbyteArray barr)
{

    char* rtn = NULL;
//    if ((env)->ExceptionCheck()) {
//        return rtn;
//    }
//    jclass clsstring = env->FindClass("Java/lang/String");
//    jstring strencode = env->NewStringUTF("utf-8");
//
//    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
//    jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0)
    {
        rtn = (char*)malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}

JNIEXPORT jlong JNICALL
Java_com_example_xiaoke_gifplayer_GifHandler_loadGif(JNIEnv *env, jclass type, jbyteArray path_) {
    char *filePath = jstringTostring(env, path_);//javastring转换成char*,文件路径
    int err;
    GifFileType *gifFileType = DGifOpenFileName(filePath, &err);//调用源码api里方法,打开gif,返回GifFileType实体
    //初始化结构体
    dGifSlurp(gifFileType);
    //实例化bean;
    GifBean *gifBean = (GifBean *) malloc((sizeof(GifBean)));
    gifBean->frame_duration = 0;
    gifBean->current_frame = 0;
    gifBean->total_frame = 0;
    gifBean->total_time = 0;
    //好比一个view打一个tag
    gifFileType->UserData = gifBean;
    //下一步,给gifbean成员变量赋值,得到当前播放时间的总时长;
    int i, j, frame_delay;
    //一帧图片
    SavedImage *frame;
    ExtensionBlock *ext;
    for (i = 0; i < gifFileType->ImageCount; i++) {
        frame = &gifFileType->SavedImages[i];
        for (j = 0; j < frame->ExtensionBlockCount; j++) {
            //找到含有延迟时间的代码块
            if (frame->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) {
                ext = &(frame->ExtensionBlocks[j]);
            }
        }
        if (ext) {
            //延迟时间1-->10ms
            frame_delay = 10 * (ext->Bytes[2] << 8 | ext->Bytes[1]);
            //得到gif总时间
            gifBean->total_time += frame_delay;
        }
    }
    //每一帧时间
    gifBean->frame_duration = gifBean->total_time / gifFileType->ImageCount;
    //总帧数
    gifBean->total_frame = gifFileType->ImageCount;
    return (long long) gifFileType;
}

mainActivity,调用

 File file=new File(Environment.getExternalStorageDirectory(),"test.gif");
        gifHandler =GifHandler.load(file.getAbsolutePath());
        //得到gif的width,height,生成Bitmap;
        int width=gifHandler.getWidth(gifHandler.getGifPoint());
        int height=gifHandler.getHeight(gifHandler.getGifPoint());
        bitmap= Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
         int nextTime=  gifHandler.updateFrame(gifHandler.getGifPoint(),bitmap);
        handler.sendEmptyMessageDelayed(1,nextTime);
    }
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            int nextTime=gifHandler.updateFrame(gifHandler.getGifPoint(),bitmap);
            handler.sendEmptyMessageDelayed(1,nextTime);
            iv_gif.setImageBitmap(bitmap);
        }
    };

ok,项目结束。如果需要源码,请联系我,现在没时间上传了。

你可能感兴趣的:(项目中小功能抽离)