imageview glide加载本地gif图片闪烁一下_Android性能优化,Startalk会话页GIF内存优化实践...

e5ae9215a8e641b369e7e3a88fa53d9e.png

1.背景

做为IM的核心部分,会话页的展示和流畅度十分影响用户体验,本次优化的内容正是会话里面的Gif图片的展示,Android原生是没有View直接支持Gif图片播放的,Startalk使用Glide+FrameSequenceDrawable实现对Gif的支持,但是在使用过程中发现了一些问题,例如在一个会话里面Gif图过多过大,IM在运行一段时间后内存吃紧,造成页面开始卡顿,甚至OOM等问题,为了解决这个问题我们通过Android Studio 3.0开始内置的Android Profiler工具来检测Memory的变化,从而发现问题所在并实施优化。

2.Android Profiler介绍

首先看一下Android Profiler共享时间线视图

62e50e828ca193693d29cae5963adfb3.png
(图片来自developer.android.com)

Android Profiler现在显示一个共享时间线视图,其中包括一个带有CPU、MEMORY和NETWORK使用情况实时图表的时间线。该窗口还包括时间线缩放控件 3,用于跳转到实时更新的按钮 4 以及显示活动状态,用户输入事件和屏幕旋转事件 5 的事件时间线,1 是连接的设备,2 当前所选进程。

3.问题分析

了解了Android Profiler后,我们通过MEMORY时间线看一下在我们进入会话页后&当会话页有较多较大的GIF时我们的IM APP内存占用对比情况,首先看我们刚进入没有GIF的会话页内存占用如下

6e897e6bf45fe1a53ed82d362c7d909d.png

说明:

•Total:当前所选进程占用的总内存大小

•Java:从Java或Kotlin代码分配的对象的内存

•Native:从C或C ++代码分配的对象的内存

•Graphics:用于图形缓冲区队列的内存

•Stack:应用程序中堆栈和Java堆栈使用的内存,这通常与您的应用运行的线程数有关

•Code:应用程序使用代码和资源的内存,例如dex字节码,优化或编译的dex代码,.so库和字体

•Others:应用程序使用的内存,系统不知道如何分类

接着我们看一下在我进入一个Gif比较多(个别Gif图很大20M左右)会话后,滑动会话页后内存占用如下图:

0833f31ff83d9e790ce560cd4313d480.png

从MEMORY时间线可以看到Native增加了将近70M,并且在显示之前已经展示过的Gif时Native内存同样还是在增长,结束会话页后内存一直保持在一定值没有下降。通过上面的分析得出的结论是在加载Gif的时候程序不断的在申请内存,前面背景中提到我们的Gif时Glide+FrameSequenceDrawable加载的,所以C&C++申请内存的操作于应该时在FrameSequence中,看一下FrameSequenceDrawable源码,发现这三个native 申请内存方法。

52956f7a89efae8756f5d4d50bb84809.png

再看一下我们程序里面是如何使用的

Glide.with(context)
        .load(url)
        .asGif()
        .toBytes()
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .dontAnimate()
        .into(new ViewTarget<LoadingImgView, byte[]>(mLoadingImgView) {
            @Override
            public void onResourceReady(byte[] resource, GlideAnimation<? super byte[]> glideAnimation) {
                FrameSequence fs = FrameSequence.decodeByteArray(resource);
                FrameSequenceDrawable drawable = new FrameSequenceDrawable(fs);
                view.setImageDrawable(drawable);
            }
 
        });

这段代码是在会话列表的adapter中执行的,FrameSequence.decodeByteArray(resource)每次这个view展示的时候都会被调用到,也就意味着每次都会申请创建 byte[] resource长度大小的内存,这也是重复显示同一个Gif时内存不断增加的原因。

接下来我们对这段代码进行优化,使用Cache策略(LruCache)确保同一个url对应一个FrameSequenceDrawable。

Glide.with(context)
        .load(url)
        .asGif()
        .toBytes()
        .diskCacheStrategy(DiskCacheStrategy.ALL)//缓存全尺寸
        .dontAnimate()
        .into(new ViewTarget<LoadingImgView, byte[]>(mLoadingImgView) {
            @Override
            public void onResourceReady(byte[] resource, GlideAnimation<? super byte[]> glideAnimation) {
                WeakReference<Parcelable> cached = new WeakReference<>(MemoryCache.getMemoryCache(url));
                if(cached.get() == null){
                    FrameSequence fs = FrameSequence.decodeByteArray(resource);
                    FrameSequenceDrawable drawable = new FrameSequenceDrawable(fs);
                    drawable.setByteCount(resource.length);
                    view.setImageDrawable(drawable);
                    MemoryCache.addObjToMemoryCache(url,drawable);
                }else {
                    if(cached.get() instanceof FrameSequenceDrawable){
                        FrameSequenceDrawable fsd = (FrameSequenceDrawable)cached.get();
                        view.setImageDrawable(fsd);
                    }
 
                }
 
            }
 
        });

其中MemoryCache为LruCache封装的工具类,同时使用了WeakReference来保证FrameSequenceDrawable更容易被回收,回收的好处是native申请的内存可以被销毁释放

protected void finalize() throws Throwable {
    try {
        if (mNativeFrameSequence != 0) nativeDestroyFrameSequence(mNativeFrameSequence);
    } finally {
        super.finalize();
    }
}

我们在Application的onTrimMemory(level)方法来清空MemoryCache里面的缓存,触发GC(备注:onTrimMemory(level)方法会在程序内存吃紧的时候回调到又不通的level级别),我们这里设置 level >= TRIM_MEMORY_RUNNING_MODERATE,这样在我们Home出程序的时候会被执行。

public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (level >= TRIM_MEMORY_RUNNING_MODERATE) {
        QIMSdk.getInstance().clearMemoryCache();
    }
}

然后我们重新通过Android Profiler查看上面同样的操作内存情况

02e16a770baf331841ab912290e316d7.png

在我退出会话页若干秒或者Home出去后,Native内存瞬间降下来了,大概回到进会话前大小。

通过Android Profiler对内存的分析我们优化了Gif的内存消耗问题,其实通过这个工具我们还能分析出程序的不足地方,本次针对的主要是Native的内存部分,而我们内存的另一大开销Java堆内存也是我们优化的重点。

问题:在分析FrameSequenceDrawable源码的时候我们发现Android7.0及以上当view隐藏的时候回调不到 setVisible方法,只做了临时处理,有知道的小伙伴可以评论回复我。

public boolean setVisible(boolean visible, boolean restart) {
    boolean changed = super.setVisible(visible, restart);
 
    //TODO 7.0及以上特殊处理 暂时没找到其他好办法
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        if(visible && !isRunning() && !restart){
            restart = true;
        }
    }
    if (!visible) {
        super.setVisible(visible, restart);
        stop();
    } else if (restart || changed) {
        stop();
        start();
    }
    return changed;
}
云趣聊​im.qunar.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Glide 是一个高效、开源、 Android设备上的媒体管理框架,它遵循BSD、MIT以及Apache 2.0协议发布。Glide具有加载本地图片、远程图片GIF动画等功能。项目地址:https://github.com/bumptech/glide 你也可以直接下载jar包:https://github.com/bumptech/glide/releases 如何使用:创建个ImageView2. 获取图片并填充到ImageView  获取远程图片: Glide.with(this).load("http://www.see-source.com/source/img/logo_01.png").into(iv);   获取本地图片Glide.with(this).load(R.drawable.logo_01).into(iv);  获取GIF动画图片Glide.with(this).load(R.drawable.image02).into(iv);image02.gif是个动画图片具体功能如下:GIF 动画的解码 :通过调用Glide.with(context).load(“图片路径“)方法,GIF动画图片可以自动显示为动画效果。如果想有更多的控制,还可以使用Glide.with(context).load(“图片路径“).asBitmap()方法加载静态图片,使用Glide.with(context).load(“图片路径“).asGif()方法加载动画图片本地视频剧照的解码: 通过调用Glide.with(context).load(“图片路径“)方法,Glide能够支持Android设备中的所有视频剧照的加载和展示缩略图的支持: 为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图Activity 生命周期的集成: 当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求转码的支持: Glide的toBytes() 和transcode() 两个方法可以用来获取、解码和变换背景图片,并且transcode() 方法还能够改变图片的样式动画的支持: 新增支持图片的淡入淡出动画效果(调用crossFade()方法)和查看动画的属性的功能OkHttp 和Volley 的支持: 默认选择HttpUrlConnection作为网络协议栈,还可以选择OkHttp和Volley作为网络协议栈其他功能: 如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化图片的宽度和高度可重新设定、缩略图和原图的缓存等功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值