PagerAdapter changed the adapter‘s contents without calling notifyDataSetChanged

The application's PagerAdapter changed the adapter's
                    contents without calling PagerAdapter#`notifyDataSetChanged`!
1. 异常发生的地方
ViewPager.java
void populate(int newCurrentItem) {
    ...
     final int pageLimit = mOffscreenPageLimit;
        final int startPos = Math.max(0, mCurItem - pageLimit);
        final int N = mAdapter.getCount();
        final int endPos = Math.min(N - 1, mCurItem + pageLimit);

        if (N != mExpectedAdapterCount) {
            String resName;
            try {
                resName = getResources().getResourceName(getId());
            } catch (Resources.NotFoundException e) {
                resName = Integer.toHexString(getId());
            }
            throw new IllegalStateException("The application's PagerAdapter changed the adapter's"
                    + " contents without calling PagerAdapter#notifyDataSetChanged!"
                    + " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N
                    + " Pager id: " + resName
                    + " Pager class: " + getClass()
                    + " Problematic adapter: " + mAdapter.getClass());
        }
    ...
}
2.分析异常原因

N 和 mExpectedAdapterCount 不相等的时候就会产生这个异常。
N为mAdapter.getCount() 最新的Adapter的数量,
mExpectedAdapterCount是之前保存的Adapter数量,应该是某个地方我的Adapter更新了,但是mExpectedAdapterCount没有更新造成的问题。

3. 自己的代码分析
//清空Fragment的缓冲,重新创建Fragment 列表填充到ViewPager里面
fun setNewData(list:List<MenuData>,viewPager: ViewPager,manager:FragmentManager){

       mData.clear()
       mData.addAll(list)
       
       //清空FragmentManager里面的所有的缓存
       var aClass = manager.javaClass
       try {
           //1.获取其mAdded字段
           var f: Field = aClass.getDeclaredField("mAdded")
           f.setAccessible(true)
           //强转成ArrayList
           //清空缓存
           (f.get(manager)as ArrayList<Fragment>).clear()

           //2.获取mActive字段
           f = aClass.getDeclaredField("mActive")
           f.setAccessible(true)
           //强转成SparseArray
           //清空缓存
           (f.get(manager) as HashMap<String, Fragment>).clear()
       } catch (e: Exception) {
           e.printStackTrace()
       }
       mViewPager = viewPager
       //希望ViewPager 初始化的时候 所有的Fragment都可以创建
       mViewPager?.offscreenPageLimit = list.size

       //设置新的Adapter 
       mViewPager?.adapter = InnerPagerAdapter(manager,mData)
       //清空父布局的所有缓存的View 
       mTabsContainer?.removeAllViews()
       Log.e("gacmy","notifyDataChange  ${list.size}")
    
       notifyDataChange()

       prePos = 0
       curPos = 0
       Log.e("gacmy","setNewData  ${list.size}")
       mViewPager?.adapter?.notifyDataSetChanged()
       setSelectTab(0,0)
   }
   
   

ViewPager设置Fragment列表第二次重新更新ViewPager列表的时候就会报错
感觉很奇怪,分析哪些地方会调用populate()这个方法
ViewPager.java 里面dataSetChange setOffscreenPageLimit 这些地方会调用
offscreenPageLimit 方法会调用 日志这个方法之后就不会调用了,应该是这里报错了

4. 原因分析

setOffscreenPageLimit 调用的时候mAdapter里的数据个数发生了变化,导致调用这个方法的时候和上一次
ViewPager缓存的Adapter的个数不一致。
上面代码设置adapter的代码 在setOffscreenPageLimit的后面,说明数据在
setOffscreenPageLimit之前发生了变化,但是mExpectedCount却没来得及更新

mData.clear() 
mData.addAll(list)

只有这里数据发生了变化,说明我的Adapter持有的引用是外部的mData,我把mData的数据增加减少了,但是却没有通知ViewPager,导致调用setOffscreenPageLimit 检测的是老数据.
下面看看Adapter的代码看看

 class InnerPagerAdapter:FragmentPagerAdapter{
        private var mFragments :
            java.util.ArrayList<MenuData>? = null
         constructor(
            fm: FragmentManager,
            fragments: java.util.ArrayList<MenuData>
        ):super(fm) {
             //这里持有外部引用
             mFragments = frgments
        }

        override fun getItem(position: Int): Fragment {
            return mFragments!![position].fragment!!
        }

        override fun getCount(): Int {
            return mFragments!!.size
        }

        override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {

        }

        override fun getItemPosition(`object`: Any): Int {
            return FragmentPagerAdapter.POSITION_NONE
        }
    }

adapter引用了外部的mData 导致mData数据跟新,ViewPager缓存的数据没有及时更新
所以很多代码为了线程安全,数据安全都会在内部持有自己的引用,不能引用外部引用导致代码的不安全性。
下面修该后的代码,adapter里的引用自己持有,不要和外部产生关联。

 mFragments = ArrayList<MenuData>()
 mFragments?.addAll(fragments)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值