最近在做项目的时候,遇到了ViewPager空白白屏问题,所有有了本篇文章,从源码分析,记一次ViewPager白屏问题。
如下图所示:

一.切换Tab3后点击Tab1,ViewPager页面出现空白:

ViewPager默认缓存1个页面,设置当前页面左右页面预加载页:
点击Tab1,日志如下:

10-18 15:07:26.195 D/xxx: initView0
10-18 15:07:26.213 D/xxx: initView1

当点击Tab1的时候会默认创建Tab2页面。
点击Tab2,日志如下:

10-18 15:07:28.490 D/xxx: initView2

当点击Tab2的时候会默认创建Tab3页面。
点击Tab3,日志如下:

10-18 15:07:30.607 D/xxx: onDestroyView0

当点击Tab3时会销毁Tab1页面。
再次点击Tab1,日志如下:

10-18 15:07:39.866  D/xxx: initView0
10-18 15:07:40.207  D/xxx: onDestroyView2

所以再次点击Tab1的时候因为之前的Tab1页面已经销毁,会出现空白。
解决方法:
默认缓存所有Tab个数的页面,如下:

viewPager.setOffscreenPageLimit(3);//3为tab的个数

那么为什么设置缓存页面个数就可以解决切换页面空白问题呢?
因为代码中使用的是FragmentPagerAdapter,viewPager.setOffscreenPageLimit(n)中n的个数会影响Fragment的onDestroyView()方法,当缓存的 fragment 超过 setOffscreenPageLimit 设置的值后, 那些 fragment 的onDestroyView()方法会回调。
因此设置为3后切换Tab的话不会调用onDestroyView()方法,不会销毁原来的Fragment也就不会出现空白页面。

二.点击页面中其他按钮刷新整个页面后,ViewPager页面出现空白:

点击页面其他按钮会刷新整个页面重新请求接口,ViewPager会重新setAdapter,此时ViewPager各个子页面都是空白,白屏状态。
通过打印日志,可知,刷新页面ViewPager再次setAdapter,其中FragmentPagerAdapter的getItem()方法并未执行,所以没有获取到相应的Fragment故出现白屏。
解决方法:
继承FragmentStatePagerAdapter替换FragmentPagerAdapter。
问题解决,那么这是为什么呢?陷入思考,FragmentStatePagerAdapter和FragmentPagerAdapter有什么区别呢?
查看源码:
关于FragmentPagerAdapter和FragmentStatePagerAdapter都有下面两个方法:

@NonNull@Overridepublic Object instantiateItem(@NonNull ViewGroup container, int position) {return super.instantiateItem(container, position);}@Overridepublic void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {super.destroyItem(container, position, object);}

对于FragmentPagerAdapter,两个方法实现如下:

@NonNullpublic Object instantiateItem(@NonNull ViewGroup container, int position) {if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}long itemId = this.getItemId(position);String name = makeFragmentName(container.getId(), itemId);Fragment fragment = this.mFragmentManager.findFragmentByTag(name);//获取Fragmentif (fragment != null) {this.mCurTransaction.attach(fragment);} else {fragment = this.getItem(position);this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));}if (fragment != this.mCurrentPrimaryItem) {fragment.setMenuVisibility(false);fragment.setUserVisibleHint(false);}return fragment;}public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}this.mCurTransaction.detach((Fragment)object);}

可以看出FragmentPagerAdapter直接通过findFragmentByTag从固定里面获取Fragment。
因为getItem(position)返回的fragment帮我们加入事务管理中并设置了一个name ,下次再进来这个fragment的时候直接根据这个name来恢复这个fragment。
在destroyItem的时候,FragmentPagerAdapter只是调用detach()方法,将这个position的Fragment的id和宿主解绑,将Fragment的View销毁,而Fragment对象还存在其实并没有把Fragment销毁。所以FragmentPagerAdapter中的Fragment一直存在内存中。
对于FragmentStatePagerAdapter,两个方法实现如下:

@NonNullpublic Object instantiateItem(@NonNull ViewGroup container, int position) {Fragment fragment;if (this.mFragments.size() > position) {fragment = (Fragment)this.mFragments.get(position);//从集合中获取Fragmentif (fragment != null) {return fragment;}}if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}fragment = this.getItem(position);//如果当前保存的mSavedState中有该位置的数据 setInitialSavedState 将数据恢复if (this.mSavedState.size() > position) {SavedState fss = (SavedState)this.mSavedState.get(position);if (fss != null) {fragment.setInitialSavedState(fss);}}while(this.mFragments.size() <= position) {this.mFragments.add((Object)null);}fragment.setMenuVisibility(false);fragment.setUserVisibleHint(false);this.mFragments.set(position, fragment);this.mCurTransaction.add(container.getId(), fragment);return fragment;}public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {Fragment fragment = (Fragment)object;if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}while(this.mSavedState.size() <= position) {this.mSavedState.add((Object)null);}this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null);this.mFragments.set(position, (Object)null);this.mCurTransaction.remove(fragment);}

可以看出,在FragmentStatePagerAdapter 里面首先通过mFragments的集合判断是否含有Fragment,如果有的话则直接返回Fragment。
在destroyItem的时候调用了remove方法,销毁了Fragment。
总结:
FragmentPagerAdapter会将每一个生成的Fragment保存在内存中,切换Fragment过程中不会销毁Fragment对象。适用于比较固定的少量的Fragment。
FragmentStatePagerAdapter中实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源。而在页面需要显示时,生成新的页面。适合页面较多,不会占据大量的内存。
参考文章:
https://www.jianshu.com/p/a97e71bc8281
https://www.jianshu.com/p/9687209cfd5f

记ViewPager使用白屏问题相关推荐

  1. 前端白屏问题_记一次白屏统计与修理

    记一次白屏统计与修复 博文原地址 白屏来源 上一版部门用的前端结构是python || lua渲染的html页面 + seajs + js模板 + less,但是这种情况发现对应不懂服务端渲染页面,或 ...

  2. 解决viewpager setCurrentItem 白屏问题

    比如你刚刚初始化viewpager数据,每个viewpager里面放的fragment,   设置currentItem  为4  可能就会白屏. 解决方案 viewPager.setOffscree ...

  3. https 请求白屏_记一次HTTPS性能优化

    为了解决部分 ios 打开 h5 网页很慢的情况(参考地址:https://developers.weixin.qq.com/community/develop/doc/000a6671efc968a ...

  4. android广告页白屏_年度整理!2056页《大厂安卓岗面试真题解析合集》火爆全网...

    前言 2020年还有最后一个月就结束了,时间一眨眼就过去了.今年面试有没有被面试官虐呢,明年跳槽想跳去哪个大厂呢,这是个问题.说实话,今年我面试也被虐了,为了明年能找到一份心怡的工作,特地的从朋友那里 ...

  5. [重磅] 让HTML5达到原生的体验 系列之中的一个 避免切页白屏

    非常多人都想.甚至曾使用HTML5开发跨平台App.而且想达到原生App的体验. 最后的结果都是无奈的放弃.HTML5貌似美好,但坑太多.想做到原生App的体验差点儿不可为. 也曾有过著名的faceb ...

  6. 消除 activity 启动时白屏、黑屏问题

    默认情况下 activity 启动的时候先把屏幕刷成白色,再绘制界面,绘制界面或多或少有点延迟,这段时间中你看到的就是白屏,显然影响用户体验,怎么消除呢? 在 Activity theme 设置sty ...

  7. as3 android白屏,Android 8.0中一些坑以及对应的解决方法

    前言 虽然 Android 9.0 都已经面世了,本篇文章写的有点迟了. 但是迟到好过不到,因此基于此这边还是记录一下项目中遇到的 Android 8.0 的坑及对应解决方法. 每次系统升级,虽然系统 ...

  8. html meta 跳转 白屏,vue使用keepAlive之后页面空白白屏?route和router什么区别呢?...

    问题描述: vue使用keepAlive之后页面空白,报错:TypeError: Cannot read property 'keepAlive' of undefined... 为了实现页面跳转之后 ...

  9. abortonerror_离线打包白屏

    同样的问题,gradle文件如下: android { compileSdkVersion 29 defaultConfig { multiDexEnabled true applicationId ...

最新文章

  1. 当当网首页——JS代码
  2. had oop 链接mysql_php – 将MySQL连接查询与OOP和对象相关联的最佳实践方法
  3. python画同心圆程序_Python Turtle:使用circle()方法绘制同心圆
  4. 数字化工厂的五大系统_如何搭建以MES系统为核心的数字化工厂?
  5. Struts标签入门
  6. JAVA项目中找不到tomcat_Tomcat启动过程中找不到JAVA_HOME解决方法
  7. 【java奇思妙想】使用多线程的思想来实现java网络编程接收和发送的问题
  8. 四阶龙格库塔法的基本思想_四阶龙格—库塔法的原理及其应用
  9. 物联网行业网络解决方案_不同行业的物联网挑战
  10. 生成 Excel + PDF 导出,用 Java 实现
  11. php 批量 挂马,php下批量挂马和批量清马代码
  12. 自动驾驶之-MATLAB环境下基于深度学习的语义分割
  13. php 正则获取邮箱后缀名,php中邮箱地址正则表达式实现与详解
  14. Windows平台精选软件工具列表-Windows绝赞应用
  15. TDM到二向箔:阿里妈妈展示广告Match底层技术架构演进
  16. 考研英语|传统文化英语高频词汇
  17. 新浪微博OAuth接口实现登录 java版
  18. 香港旅游攻略香港旅游购物指南
  19. LeetCode:121(Python)—— 买卖股票的最佳时机(简单)
  20. 文本挖掘 - 文档模型

热门文章

  1. 桌面上IE图标不见了怎么办?
  2. MINIUI基本学习笔记
  3. 电脑快捷键:关于shift键的11个实用技巧
  4. HarmonyOS应用开发第一次培训
  5. 计算机主机和cpu的区别,主机与cpu的区别
  6. 软件测试基础知识总结(一)
  7. ReactEurope Conf 参会感想
  8. 子女通过TeamViewer远程帮助父母解决手机使用问题
  9. 下面属于python内置对象的有哪些_python练习题-写一个函数,打印所有包含copy方法的内置对象...
  10. struts2之token,类型转换和数据校验