Android列表内存优化

android列表内存优化

背景

项目中有一个列表,准确的说是图片列表(整个列表都是图片)。可想而知占用的内存会很大。有什么优化的方法呢?

解决办法

对于这种场景,简单分析下。

  1. 首先图片肯定是通过图片加载框架执行加载的。
  2. 图片加载框架对于内存有一个最大的上限
  3. 磁盘缓存暂时不管,只分析内存

那么图片框架是如何和列表结合,实现图片的动态加载的呢?

就是说在不超过图片框架内存上限的前提下,最近使用的图片都会在内存中保存。这个一个限定值,总不能每次都在最大值边缘运行吧,还是要尽可能的优化。

以Picasso为例,Picasso默认会使用设备的15%的内存作为内存图片缓存。遇到上述的图片列表,内存一直占用在15%的上限肯定不行。

解决办法:列表item在不可见的状态下,尽可能的回收内存。

ImageView Bitmap有一个recycle()方法用于内存回收。重写ImageView的onDetachedFromWindow方法,在它从屏幕中消失时回调,去掉drawable引用,能加快内存的回收。

public class RecyclerImageView extends ImageView
{ 
    ...

    @Override    
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        setImageDrawable(null);   
    }
}

踩过的坑

通过上面的方法,好像减少了不少内存的使用(内存使用的最大值减小)。

但是,发现一个新的问题:在Item中存在NetCacheImageView(或者类似框架控件)时,偶尔会出现将一个条目刚刚滑动到看不见,然后将其拉回,其中的图片变为空白。

为什么会这样呢?

为了管理界面显示引用,框架扩展了ImageView中的setImageDrawable方法。在其中对旧的ImageDrawable引用计数减一,新的ImageDrawable计数加一。然后,为了在ImageView退出屏幕时及时回收Bitmap,扩展了onDetachedFromWindow方法,在其中调用setImageDrawable(null),以回收当前显示的Bitmap。至此之前我的关注点一直在图片加载的实现上,理清楚这个机制之后才考虑到是不是回收时机出了问题。

对RecyclerView进行分析之后我发现,当一个item被滑动到刚好看不见的位置时,触发了该item及其子View的onDetachedFromWindow,同样也就调用了setImageDrawable(null)。但是,RecyclerView.Adapter.onViewRecycled方法没有立刻被调用,而要等到继续滑动RecyclerView时才调用。也就是说,RecyclerView没有立即回收已经不在显示区域的item。如果此时将该item拉回,也不会再调用RecyclerView.Adapter.onBindViewHolder,也就是图片消失之后就不会再显示了。

分析:

说简单点就是:recyclerview源码就是最多加载两屏幕的view,就是说当前屏幕的一整屏幕view和列表上下两端的部分view。

但是,onDetachedFromWindow在Item view移出屏幕后就开始执行回收(drawable置为null,使bitmap更容易被回收)

这就是问题所在,当前屏幕上方的view图片引用被清空,可能被回收。如果滑动到此位子,recyclerview不会触发刷新。因此导致图片空白。

解决办法:

已经分析的很清楚了,相信答案也出来了。可以把setImageDrawable放在recyclerview的回收时释放,即onViewRecycled方法中。在adapter中重写此方法。

@Override
    public void onViewRecycled(VideoViewHolder holder) {
        if (holder != null && holder.getItemViewType() == VideoIndication.TYPE_VIDEO) {
            holder.imgThumb.setImageDrawable(null);
        }
        super.onViewRecycled(holder);
    }

你可能感兴趣的:(Android列表内存优化)