Android ViewPager加载多Fragment致Activity启动慢ANR,getItem报错如何解决
问题根源
- 执行时序错误:你通过
Handler(getMainLooper()).post()提交的任务会被加入主线程消息队列尾部等待执行,而onCreate里的fragments.addAll()是同步执行的代码,执行合并操作时两个分批创建Fragment的任务根本还未运行,所以fragments列表为空,getItem调用时自然触发下标越界报错。 - 优化方向错误:你完全不需要提前创建所有30个Fragment,ViewPager的PagerAdapter本身自带按需加载机制,默认仅会缓存当前页和左右相邻1-2个Fragment,提前创建全部属于完全不必要的性能浪费,反而会拖慢启动速度、增大ANR风险。
解决方案
方案1:快速修复当前报错(不推荐,仅解决崩溃问题)
调整执行时序,等两个异步创建任务都执行完成后,再合并列表、设置ViewPager的Adapter:
@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 先初始化ViewPager,暂时不设置Adapter viewPager = findViewById(R.id.viewpager); new Handler(getMainLooper()).post(new Runnable() { @Override public void run() { for (int i = 0; i <= 14; i++){ fragmentsType1.add(QuestionFragment1.newInstance(questions.get(i), i)); } checkAndInitAdapter(); } }); new Handler(getMainLooper()).post(new Runnable() { @Override public void run() { for (int i = 15; i <= 29; i++){ fragmentsType2.add(QuestionFragment2.newInstance(questions.get(i), i)); } checkAndInitAdapter(); } }); } private synchronized void checkAndInitAdapter() { // 两个任务都执行完成再初始化Adapter if (fragmentsType1.size() == 15 && fragmentsType2.size() == 15) { fragments.addAll(fragmentsType1); fragments.addAll(fragmentsType2); viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager())); } }
方案2:彻底优化启动速度(推荐,从根源解决ANR问题)
放弃提前创建所有Fragment的思路,直接在Adapter的getItem方法中按需创建Fragment,不需要维护全局的Fragment列表:
// Adapter代码修改为如下 @Override public Fragment getItem(int position) { if (position <= 14) { return QuestionFragment1.newInstance(questions.get(position), position); } else { return QuestionFragment2.newInstance(questions.get(position), position); } } @Override public int getCount() { return questions.size(); // 直接返回题目列表长度即可 }
这种方案下Activity启动时仅会创建当前需要展示的1-3个Fragment,启动耗时会从3秒降到数百毫秒级别,完全避免ANR风险。
额外优化建议
从你提供的Fragment代码来看,还可以做如下优化进一步降低卡顿:
Html.fromHtml解析标题的操作可以提前放到子线程完成,解析结果存在Question对象中,不需要每次创建Fragment时再在主线程解析- 选项图片不要直接调用
setImageResource加载,改用图片加载库异步加载,避免主线程阻塞 - 使用
FragmentPagerAdapter时传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT参数,减少不必要的Fragment生命周期回调
内容的提问来源于stack exchange,提问作者Riggel
相关产品推荐
相关产品推荐

