美文网首页Android
ViewPager页面过多导致OOM(内存溢出)

ViewPager页面过多导致OOM(内存溢出)

作者: 小白cz | 来源:发表于2018-04-07 01:02 被阅读0次

前言

ViewPager是一个经常使用的组件,但是当页面越来越多时,由于各种原因(如内存泄露等等),会导致崩溃(内存溢出),好了,进入正题

本文出现的vp为ViewPager简写

vp页面的添加&删除

由于vp有预加载功能,会有两种加载情况(可能不止?)
1.先加载当前页,再从左到右加载or删除页面(左滑右滑)
2.跳转到某页的情况(如第1页跳转第10页),会优先加载跳转页&预加载页,再删除之前的旧页面(如加载10,9,11再删除1,2)

即真正需要用到的页面数其实是:预加载页 + 1个当前页+1缓存页

//把一侧预加载设置为2,即需要用到的页面为 2 * 2 + 1 + 1 = 6 个页面,vp预加载默认为1即缓存4个页面足以
vp.setOffscreenPageLimit(2);

那么我们可以用一个定长数组把这些页面缓存起来,然后通过复用这些页面达到资源合理利用,先贴代码
该适配器,弱引用防止Context泄漏,T为页面数据模型,H为页面Holder,View数组为缓存页面的数组
mUnRemoveTags这个int集合是个重点,用于标记vp调用destroyItem时是否删除页面,跟vp从左到右添加or删除有关
提供两个构造方法,一个为默认预加载为1,另一个可自行设置也加载数

public abstract class RecyclePagerAdapter<T, H extends RecyclePagerAdapter.PagerHolder> extends PagerAdapter {

    private final WeakReference<Context> mContext;

    private final List<T> mInfos;

    private final View[] mCacheView;

    //不删除记号,用于destroyItem时不清除view
    private final Set<Integer> mUnRemoveTags = new HashSet<>();

    public RecyclePagerAdapter(Context context, List<T> infos) {
        this(context, infos, 1);
    }

    /**
     * @param pagerLimit 预加载一侧页数(即预加载总数为pagerLimit * 2)
     */
    public RecyclePagerAdapter(Context context, List<T> infos, int pagerLimit) {
        if(infos == null)
            throw new NullPointerException();
        if(pagerLimit < 1)
            pagerLimit = 1;
        this.mContext = new WeakReference<>(context);
        this.mInfos = infos;
        //(pagerLimit * 2)(预加载数) + 1(当前页) + 1(缓存页)
        mCacheView = new View[(pagerLimit + 1) * 2];
    }
    ......
}

再添加两个获取页面数据&Context的方法

{
    ...
    public List<T> getPagerInfos(){
        return mInfos;
    }

    public Context getContext(){
        return mContext.get();
    }
    ...
}

现在再来看下PagerHolder,这个跟ListView的Holder差不多,就不多解释

public static class PagerHolder{
    public final View view;
    public PagerHolder(View view) {
        view.setTag(this);
        this.view = view;
    }
}

国际惯例getCount,isViewFromObject,都是原本的写法~
基本需要数据&方法都差不多了,现在来看看重点,添加&删除页面,这里我们先添加三个抽象方法供子类实现,与recycleview适配器类似,用于创建view,holder和绑定view数据,释放view

{
    ....
    /**
     * 创建一个Holder
     * @param position 页面下标
     * @return holder
     */
    protected abstract H onCreaterPagerHolder(Context context, int position);

    /**
     * 绑定页面数据
     * @param info 页面数据
     * @param position 页面下标
     */
    protected abstract void onBindPagerHolde(H holder, T info, int position);

    /**
     * 用于释放页面destroyItem调用且移除时调用
     */
    protected void onReleasePagerHolde(H holder, int position){}
    ....
}

接下来就到添加&删除页面了

1.添加view,通过position对缓存长度取模确定缓存下标,再判断是否需要新建页面,若页面已存在vp中,则记录下标,到destroyItem时不删除该页面,(复用)
2.删除view,通过已记录缓存下标判(mUnRemoveTags)断是否删除页面

{
    ......
    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        //获取缓存下标
        int index = position % mCacheView.length;

        T info = mInfos.get(position);
        H holder;

        if(mCacheView[index] == null){
            holder = onCreaterPagerHolder(mContext.get(), position);
            mCacheView[index] = holder.view;
        }else{
            holder = (H) mCacheView[index].getTag();
        }

        //获取容器是否存在该View
        int i = container.indexOfChild(holder.view);
        if(i != -1)
            //存在则记录缓存下标
            mUnRemoveTags.add(index);
        else
            container.addView(holder.view);

        onBindPagerHolde(holder, info, position);

        return holder.view;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        int index = position % mCacheView.length;
        //检查是否在不删除标记内,是则不移除view,否则移除view
        if(!mUnRemoveTags.contains(index)) {
            View view = mCacheView[index];
            onReleasePagerHolde((H) view.getTag(), position);
            container.removeView(view);
        }else {
            mUnRemoveTags.remove(index);
        }
    }
    ......
}

好了,到这里,vp复用就完了,目前,缺陷是只能用于同一种布局,用于多布局的话可以配合recycleview Or ListView等等使用

完整适配器代码

/**
 * Created by xiaobaicz
 */
public abstract class RecyclePagerAdapter<T, H extends RecyclePagerAdapter.PagerHolder> extends PagerAdapter {

    private final WeakReference<Context> mContext;

    private final List<T> mInfos;

    private final View[] mCacheView;

    //不删除记号,用于destroyItem时不清除view
    private final Set<Integer> mUnRemoveTags = new HashSet<>();

    public RecyclePagerAdapter(Context context, List<T> infos) {
        this(context, infos, 1);
    }

    /**
     * @param pagerLimit 预加载一侧页数(即预加载总数为pagerLimit * 2)
     */
    public RecyclePagerAdapter(Context context, List<T> infos, int pagerLimit) {
        if(infos == null)
            throw new NullPointerException();
        if(pagerLimit < 1)
            pagerLimit = 1;
        this.mContext = new WeakReference<>(context);
        this.mInfos = infos;
        //(pagerLimit * 2)(预加载数) + 1(当前页) + 1(缓存页)
        mCacheView = new View[(pagerLimit + 1) * 2];
    }

    @Override
    public int getCount() {
        return mInfos.size();
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        //获取缓存下标
        int index = position % mCacheView.length;

        T info = mInfos.get(position);
        H holder;

        if(mCacheView[index] == null){
            holder = onCreaterPagerHolder(mContext.get(), position);
            mCacheView[index] = holder.view;
        }else{
            holder = (H) mCacheView[index].getTag();
        }

        //获取容器是否存在该View
        int i = container.indexOfChild(holder.view);
        if(i != -1)
            //存在则记录缓存下标
            mUnRemoveTags.add(index);
        else
            container.addView(holder.view);

        onBindPagerHolde(holder, info, position);

        return holder.view;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        int index = position % mCacheView.length;
        //检查是否在不删除标记内,是则不移除view,否则移除view
        if(!mUnRemoveTags.contains(index)) {
            View view = mCacheView[index];
            onReleasePagerHolde((H) view.getTag(), position);
            container.removeView(view);
        }else {
            mUnRemoveTags.remove(index);
        }
    }

    /**
     * 创建一个Holder
     * @param position 页面下标
     * @return holder
     */
    protected abstract H onCreaterPagerHolder(Context context, int position);

    /**
     * 绑定页面数据
     * @param info 页面数据
     * @param position 页面下标
     */
    protected abstract void onBindPagerHolde(H holder, T info, int position);

    /**
     * 用于释放页面destroyItem调用且移除时调用
     */
    protected void onReleasePagerHolde(H holder, int position){}

    public List<T> getPagerInfos(){
        return mInfos;
    }

    public Context getContext(){
        return mContext.get();
    }

    public static class PagerHolder{
        public final View view;
        public PagerHolder(View view) {
            view.setTag(this);
            this.view = view;
        }
    }

}

简单用法

/**
 * Created by xiaobaicz
 */
public class MyAdapter extends RecyclePagerAdapter<Integer, MyAdapter.Holder> {

    public MyAdapter(Context context, List<Integer> infos) {
        super(context, infos);
    }

    public MyAdapter(Context context, List<Integer> infos, int pagerLimit) {
        super(context, infos, pagerLimit);
    }

    @Override
    protected Holder onCreaterPagerHolder(Context context, int position) {
        return new Holder(View.inflate(context, R.layout.pager, null));
    }

    @Override
    protected void onBindPagerHolde(Holder holder, Integer info, int position) {
        holder.view.setBackgroundColor(info);
    }

    static class Holder extends RecyclePagerAdapter.PagerHolder {
        Holder(View view) {
            super(view);
        }
    }

}

相关文章

  • ViewPager页面过多导致OOM(内存溢出)

    前言 ViewPager是一个经常使用的组件,但是当页面越来越多时,由于各种原因(如内存泄露等等),会导致崩溃(内...

  • 说说内存溢出?

    哪些情况下会导致oom问题? 基本概念 首先明确一点,内存泄漏和内存溢出是不同的,但是过多的内存泄漏会导致内存溢出...

  • Android 性能优化(一)

    Android作为一种移动设备,内存和CPU资源都是受限的. 过多地使用内存会导致内存溢出(OOM),过多的使用C...

  • android性能优化

    android程序过多的使用内存会造成内存溢出(OOM),过多的使用CPU资源(一般指大量的耗时工作),会导致卡顿...

  • 艺术开发探索十五章笔记

    Android性能优化 Android不可能无限制的使用内存和CPU资源,过多的使用内存会导致内存溢出,即OOM。...

  • 内存泄漏和内存溢出的区别

    内存溢出(oom) : 1.大家应该都知道oom异常是什么吧?对他是因为内存溢出导致的,那你们想过他为什么溢出...

  • Android内存优化—内存优化总结

    内存问题 内存抖动:导致GC导致卡顿 内存泄漏:导致频繁GC,可用内存减少 内存溢出:导致OOM 工具排查 AS中...

  • Android性能优化(◍˃ᗜ˂◍)✩

    Android作为移动设备,内存和CPU的性能上都受到了一定的限制。 ♛ 过多的使用内存:导致程序内存溢出(OOM...

  • Android OOM之内存泄漏详解

    OOM(OutOfMemory)就是我们平时所碰到的内存溢出,而内存泄漏的最终后果就是导致OOM。内存泄漏是造成应...

  • 软件开发的一些概念

    OOM:内存溢出

网友评论

    本文标题:ViewPager页面过多导致OOM(内存溢出)

    本文链接:https://www.haomeiwen.com/subject/zwhshftx.html