IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> ViewPager+Fragment首页布局的加载优化 -> 正文阅读

[移动开发]ViewPager+Fragment首页布局的加载优化

ViewPager+Fragment首页布局的加载优化

简介

VIewPager+Fragment是多数APP的首页布局,也是用户打开app后除了广告外第一个看到的页面,所以如果能对ViewPager+Fragment的布局有所优化,就是对app的冷启动时长有所优化。

本文通过设置ViewPager的setOffscreenPageLimit参数和提供适配ViewPager的Fragment来优化加载耗时。

优化思路

setOffscreenPageLimit(int limit)

setOffscreenPageLimit(int limit)是ViewPager提供的一个方法,用于控制提前加载页面的数量。不设置则默认参数是1。

offScreen就是屏幕外的意思,为了用户左右滑动到其他页面时能不卡顿,VIewPager允许提前加载左右相邻一定数目的页面(也就是page),这样切换到其他页面时可以看到加载好的页面。而limit参数就是预加载页面的数目,设置后会加载左右各limit个页面

举个例子,一个ViewPager+ABCDE五个Fragment,默认一开始在C Fragment。
a) 如果设置setOffscreenPageLimit(1),则会提前加载BCD三个
b)如果设置setOffscreenPageLimit(2),则会提前加载ABCDE五个
c) 如果设置setOffscreenPageLimit(1),而从C滑动到D时,E也会被预加载,而B可能会被destroy,也可能只是被remove,具体看adapter实现

优化点
最理想的情况是,启动时我只想加载用户能看到的那个页面,在首帧展示完成后,再利用空闲的时间来预加载剩余的页面,即,我们想达到setOffscreenPageLimit(0)的效果。
可惜的是,ViewPager并不支持设置为0,就算传入了0,也会在内部被纠正为默认值1,这个设计可能是为了用户操作更流畅吧。
既然不能设置为0,我们就退而求其次,设置limit为1,做到启动时尽可能少地加载页面。优化措施大概如下

1、开始时设置setOffscreenPageLimit(1)
2、对点击滑动等可能导致页面切换的操作,需要阻塞并设置setOffscreenPageLimit为最大值,保证全部Fragment被加载了,否则会有Fragment生命周期执行到destroy的风险,
3、在主线程空闲的时候,发消息在主线程执行全量加载,即设置setOffscreenPageLimit为最大值的操作,这样可以做到延迟预加载,减少对用户体验的影响。主线程是否空闲,一是可以把消息丢到idlehandler中处理,但如果handler一直繁忙,会有不会预加载的风险。二是发送延迟固定时间的消息来处理,比如延迟5s来执行,相当于预留了5s给其他业务操作,5s后再触发全量预加载

提供专门用于ViewPager的Fragment

setOffscreenPageLimit无法做到只加载可见页面,那么我们是否可以换个思路,尽量减少加载Fragment时候的耗时,进而做到近似的效果?
要想从这个角度优化,我们需要先知道ViewPager是怎么加载Fragment的
网上关于ViewPager源码的介绍已经是汗牛充栋了,我不展开细讲,只讲优化相关的流程

1、ViewPager#setAdapter时,会触发Fragment对象的创建,但此时Fragment的生命周期还没执行
2、ViewPager#setAdapter后,会触发一次页面绘制,即会有一次doFrame消息的执行,关键流程如下

doFrame
ViewPager#onMeasure
ViewPager#populate
adapter#finishUpdate
FragmentManager#moveToState
Fragment生命周期到onResume

我再贴一下Fragment的生命周期
在这里插入图片描述
总的来说,ViewPager加载Fragment时,会执行Fragment的
onAttach、onCreate、onCreateView、onActivityCreated、onStart、onResume
因为offScreenPageLimit不能为0,那么就存在对用户不可见的Fragment,也会在首帧时被执行。那么我们能否增加判断,只有当Fragment可见时,才执行onAttch到onResume等方法呢?
答案是基本可以的,判断Fragment是否对用户可见用userVisibleHint标记即可,我们只需要把onAttch到onResume中首次加载才会用到的代码归到一个方法里,在页面可见时再触发即可。
说太多了,直接上代码吧

public abstract class FragmentForViewPager extends Fragment {
	private boolean visible = false;
	private boolean loadDone =false;
	private boolean firstOnResumeDone = false;

	public abstract void loadForViewPager();
	
	public abstract void normalOnResume();
	
	@Override
	public void setUserVisibleHint(boolean isVisibleToUser) {
		super. setUserVisibleHint(isVisibleToUser);
		this.visible = isVisibleToUser;
	}
	
	// onResume方法加final限制,不允许子类重写onResume,而是改为用normalOnResume和loadForViewPager
	// 从其他页面回来触发的onResume放到normalOnResume中,而ViewPager触发的onResume则放到loadForViewPager中
	@Override
	public final void onResume() {
		super.onResume();
		// 对onResume做分流,只有第一次onResume,即ViewPager触发的onResume时
		// 才有可能触发loadForViewPager。其他情况执行normalOnResume
		if (!firstOnResumeDone) {
			// 只有当前Fragment是可见的,才执行loadForViewPager
			if (visible) {
				loadForViewPager();
				loadDone=true;
			}	
			firstOnResumeDone = true;
		} else {
			// 因为onResume会被很多情况调用,所以onResume里面并非所有操作都需要在ViewPager触发的onResume里执行
			// 所以我们可以把其他情况放到normalOnResume处理
			normalOnResume();
		}
	}

	// onDestroy后记得赋值为初始值,因为有可能Fragment被onDestroy了,但Fragment实例还在,
	// 这时系统可能会复用实例,在同一个实例上执行onCreate到onResume,如果此时不重新赋值,会导致流程失败
	@Override
	public void onDestroy() {
		super.onDestroy();
		loadDone=false;
		firstOnResumeDone=false;
	}
	
	// 用户看到这个Fragment前,程序需要调用forceLoad保证loadForViewPager一定要被执行一次,
	// 否则onResume会直接走到normaleOnResume,导致代码没有被正确初始化
	public boolean forceLoad() {
		if (!loadDone) {
			loadForViewPager();
			loadDone=true;
		}
	}

}

我们把Fragment从onAttach到onResume的生命周期拆分为两部分,loadForViewPager和normalOnResume,具体拆分如下

Fragment的onAttach到onStart中的初始化逻辑
loadForViewPager
Fragment的onResume中初始化需要的逻辑
Fragment的onResume中非初始化相关的逻辑
normalOnResume

拆分后,我们控制loadForViewPager只有在用户可见时才加载,只有loadForViewPager执行过后,后续onResume才走normalOnResume
流程变动如下
优化前

doFrame
onAttach到onStart
初始化相关的onResume逻辑
非初始化的onResume逻辑

优化后

yes
no
no
yes
doFrame
当前Fragment是否用户可见
loadForViewPager
绘制首帧
主线是否空闲
forceLoad
结束

优化点
1、子类不再需要处理userVisibleHint。很多针对ViewPager+Fragment的文章都介绍通过在setUserVisibleHint里面加逻辑来减少加载耗时,但这样与显得代码很繁杂,因为setUserVisibleHint会被频繁调用,所以需要加是否可见、是否第一次可见等状态,优化后的Fragment子类不再需要获取userVisibleHint状态。内部会自动处理好
2、减少加载不可见Fragment时的耗时,这个是因为onAttach到onStart被架空了,部分onResume逻辑也被架空了,导致首次加载的内容减少了。

总结

针对VIewPager+Fragment的布局加载,本文提供了两种思路进行优化,一是对offscreenPageLimit的动态设置,二是提供了专用于ViewPager的Fragment。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:54:07  更:2022-04-18 17:56:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 21:29:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码