在讲代码之前理解三个概念,以便理解懒加载的实现过程,当前Fragment、目标Fragment和缓存Fragment。
例如:页面有三个Fragment:Fragment1、Fragment2 、Fragment3,当用户在Fragment1上进行操作,打算跳到Fragment2中,则三个角色分别为
- 当前Fragment:当前用户操作的fragment,即Fragment1。
- 目标Fragment:要切换的fragment,即Fragment2。
- 缓存Fragment:预加载的Fragment,即Fragment3。
首先看定义的三个成员变量:
boolean mIsViewCreated=false;//Fragment是否已经创建,
boolean mIsFirstVisiable=true;//Fragment是否第一次可见
boolean mCurrentVisiableState=false;//标记保存Fragment的可见状态,表示当前Fragment是否分发过, 可见到不可见 不可见到可见才可以调用 disPatchVisibaleHint 防止重复调用
step1:setUserVisibleHint方法的逻辑处理
因为setUserVisibleHint方法是最先执行的在此先看他的实现
//与生命周期无关的函数
/**
- onCreateView在此方法之后 第一次时 mIsViewCreated=false
- @param isVisibleToUser
/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
//用户是否可见
Log.e(TAG,“setUserVisibleHint----”+isVisibleToUser);
super.setUserVisibleHint(isVisibleToUser);
/* - 1:分发的前提是FragmentView已经创建,因为setUserVisibleHint在fragment 的声明周期之前执行,
- 如果页面没创建就去分发容易造成空指针异常
- 2:因为此方法是由setUserVisibleHint方法调用,而setUserVisibleHint方法由系统的Viewpager调用多次,我们没办法控制它,
- 但是我们可以控制disPatchVisibaleHint的分发
*/
if (mIsViewCreated){
//只有Fragment创建了后才进行分发
if (isVisibleToUser&& !mCurrentVisiableState){//防止重复调用disPatchVisibaleHint !mCurrentVisiableState表示没有分发过时才分发
//用户可见时进行分发事件
disPatchVisibaleHint(true);
}else if (mCurrentVisiableState&& !isVisibleToUser){
//用户不可见时不分发事件
disPatchVisibaleHint(false);
}
}
}
/**
- 分发可见
- 调用的前提:可见–》不可见 或者不可见–》可见
- @param isVisiable
*/
private void disPatchVisibaleHint(boolean isVisiable) {
Log.e(TAG,“disPatchVisibaleHint----”+isVisiable);
if (mCurrentVisiableState==isVisiable){
//防止调用两次更新,存在当 mCurrentVisiableState=isVisiable;执行前setUserVisibleHint可能被调用两次
return;
}
mCurrentVisiableState=isVisiable;//进行赋值操作
if (isVisiable){
//可见
if (mIsFirstVisiable){
mIsFirstVisiable=false;
//处理第一次可见时
//公共方法,由子类实现
onFragmentFirstVisiable();
}
//复写onFragmentResume分发事件,网路请求
onFragmentResume();
//对viewpager嵌套使用时处理子的fragment
dispatChChildVisiableState(true);
}else {
//复写onFragmentPause终止数据请求
onFragmentPause();
//对viewpager嵌套使用时处理子的fragment
dispatChChildVisiableState(false);
}
}
/**
- 下面抽取三个方法定义成public方法是因为:
- 父类不知道子类要不要处理可见和不可见,因为子类有可能不需要懒加载,此时子类什么也不需要做
- 当需要懒加载时,才需要复写这三个方法。
- 因此这3个方法是非必须实现的方法,不能定义成抽象方法
*/
/**
- 第一次可见时特殊处理
*/
public void onFragmentFirstVisiable() {
Log.e(TAG,“onFragmentFirstVisiable”);
}
/**
- 不可见时处理相关动作 停止数据的加载
*/
public void onFragmentPause() {
Log.e(TAG,“onFragmentPause”);
}
/**
- 表面可见时 加载数据
*/
public void onFragmentResume() {
Log.e(TAG,“onFragmentResume”);
}
step2:onCreateView函数的实现
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
TAG=getClass().getSimpleName()+“----”;
Log.e(TAG,“onCreateView”);
//两个抽象方法,让子类实现initView和getLayoutRes
if (mRootView==null) {
mRootView=inflater.inflate(getLayoutRes(),null);
}
initView(mRootView);
//1:创建了Fragment 控制下面分发的前提,因为分发事件由setUserVisibleHint方法控制,而setUserVisibleHint最先执行
mIsViewCreated=true;
//2:对于默认Fragment的加载,可以在此分发一下,可见才分发
if (getUserVisibleHint()&&!isHidden()){
//可见
disPatchVisibaleHint(true);
}
return mRootView;
}
step3: onPause方法的实现
这里需要描述一下场景,当从fragment跳转到另一个Activity时需要做的处理,
只需对当前用户可见的Fragment的数据加载进行终止
@Override
public void onPause() {
Log.e(TAG,“onPause”);
if (mCurrentVisiableState&&getUserVisibleHint()){
disPatchVisibaleHint(false );
}
super.onPause();
}
step4: onResume方法的实现
这里需要描述一下场景,经过Step3后,又再次返回到Frangment所在的Activity时,只需要对当前用户可见的Fragment的数据加载
@Override
public void onResume() {
Log.e(TAG,“onResume”);
//只有当前可见的Fragment 才更新shuju 点击home键又返回
if (!mCurrentVisiableState&&getUserVisibleHint()&&!isHidden() ){
disPatchVisibaleHint(true );
}
super.onResume();
}
step5: onDestroyView方法的实现
主要是对控制变量进行恢复初始值操作##
@Override
public void onDestroyView() {
Log.e(TAG,“onDestroyView”);
super.onDestroyView();
//将所有的变量复位
mIsFirstVisiable=true;
mCurrentVisiableState=false;
mIsViewCreated=false;
}
step6:onHiddenChanged方法的实现
@Override
//与生命周期无关的函数
//在使用FragmentTransaction
的hidden
和show
方法管理fragment时,是不会走Fragment正常的生命周期,
// 而是走onHiddenChanged
方法,此方法可以监听Fragment 的显示与隐藏。
public void onHiddenChanged(boolean hidden) {
Log.e(TAG,“onHiddenChanged”);
// FragmentTransaction管理Fragment时
super.onHiddenChanged(hidden);
if (hidden){
disPatchVisibaleHint(false);
}else {
disPatchVisibaleHint(true);
}
}
到此与懒加载相关的六个方法的逻辑已经处理完毕,详细原理请看代码中的注解,再此不做过多赘述,在step1中的disPatchVisibaleHint
方法中处理ViewPager嵌套使用时,对于子ViewPager
中的Frangment处理交给了dispatChChildVisiableState
方法,那这个处理过程是怎么什么样子的呢?请接着往下看。
4:懒加载之进阶ViewPager+Fragment的
嵌套使用时子ViewPager的Frangment如何实现懒加载
先说原理:很简单,就是根据当父的Fragment对用户的可见状态,去处理子ViewPager中Fragment的事件分发。就是在上面disPatchVisibaleHint
方法中
代码实现:
/**
- ViewPage嵌套使用时处理子的Fragment懒加载逻辑
- 通过getChildFragmentManager();获取子Fragment
- @param visiable
*/
private void dispatChChildVisiableState(boolean visiable){
Log.e(TAG,"dispatChChildVisiableState ===== " + visiable);
FragmentManager childFragmentManager = getChildFragmentManager();
List childFragmentManagerFragments = childFragmentManager.getFragments();
if (childFragmentManagerFragments != null) {
for (Fragment fragment : childFragmentManagerFragments) {
//进行类型校验,只有继承了LazyFragment才进行懒加载的处理
if (fragment instanceof LazyFragment&&!fragment.isHidden()&&fragment.getUserVisibleHint()){
((LazyFragment) fragment).disPatchVisibaleHint(visiable);
}
}
}
}
5:最后附上封装好的LazyFragment
的完整代码和下载地址:
public abstract class LazyFragment extends Fragment {
public String TAG=“—”;
View mRootView;
boolean mIsViewCreated=false;//Fragment是否已经创建,
boolean mIsFirstVisiable=true;//Fragment是否第一次可见
boolean mCurrentVisiableState=false;//标记保存Fragment的可见状态,表示当前Fragment是否分发过 可见到不可见 不可见到可见才可以调用 disPatchVisibaleHint 防止重复调用
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
TAG=getClass().getSimpleName()+“----”;
Log.e(TAG,“onCreateView”);
//两个抽象方法,让子类实现initView和getLayoutRes
if (mRootView==null) {
mRootView=inflater.inflate(getLayoutRes(),null);
}
initView(mRootView);
//1:创建了Fragment 控制下面分发的前提,因为分发事件由setUserVisibleHint方法控制,而setUserVisibleHint最先执行
mIsViewCreated=true;
//2:对于默认Fragment的加载,可以在此分发一下,可见才分发
if (getUserVisibleHint()&&!isHidden()){
//可见
disPatchVisibaleHint(true);
}
return mRootView;
}
@Override
public void onResume() {
Log.e(TAG,“onResume”);
//只有当前可见的Fragment 才更新shuju 点击home键又返回
if (!mCurrentVisiableState&&getUserVisibleHint()&&!isHidden() ){
disPatchVisibaleHint(true );
}
super.onResume();
}
@Override
public void onPause() {
Log.e(TAG,“onPause”);
if (mCurrentVisiableState&&getUserVisibleHint()){
disPatchVisibaleHint(false );
}
super.onPause();
}
@Override
public void onDestroyView() {
Log.e(TAG,“onDestroyView”);
super.onDestroyView();
//将所有的变量复位
mIsFirstVisiable=true;
mCurrentVisiableState=false;
mIsViewCreated=false;
}
//与生命周期无关的函数
/**
- onCreateView在此方法之后 第一次时 mIsViewCreated=false
- @param isVisibleToUser
/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
//用户是否可见
Log.e(TAG,“setUserVisibleHint----”+isVisibleToUser);
super.setUserVisibleHint(isVisibleToUser);
/* - 1:分发的前提是FragmentView已经创建,因为setUserVisibleHint在fragment 的声明周期之前执行,
- 如果页面没创建就去分发容易造成空指针异常
- 2:因为此方法是由setUserVisibleHint方法调用,而setUserVisibleHint方法由系统的Viewpager调用多次,我们没办法控制它,
- 但是我们可以控制disPatchVisibaleHint的分发
*/
if (mIsViewCreated){
//只有Fragment创建了后才进行分发
if (isVisibleToUser&& !mCurrentVisiableState){//防止重复调用disPatchVisibaleHint !mCurrentVisiableState表示没有分发过时才分发
//用户可见时进行分发事件
disPatchVisibaleHint(true);
}else if (mCurrentVisiableState&& !isVisibleToUser){
//用户不可见时不分发事件
disPatchVisibaleHint(false);
}
}
}
@Override
//与生命周期无关的函数
//在使用FragmentTransaction
的hidden
和show
方法管理fragment时,是不会走Fragment正常的生命周期,
// 而是走onHiddenChanged
方法,此方法可以监听Fragment 的显示与隐藏。
public void onHiddenChanged(boolean hidden) {
Log.e(TAG,“onHiddenChanged”);
// FragmentTransaction管理Fragment时
super.onHiddenChanged(hidden);
if (hidden){
disPatchVisibaleHint(false);
}else {
disPatchVisibaleHint(true);
}
}
/**
- 分发可见
- 调用的前提:可见–》不可见 或者不可见–》可见
- @param isVisiable
*/
private void disPatchVisibaleHint(boolean isVisiable) {
Log.e(TAG,“disPatchVisibaleHint----”+isVisiable);
if (mCurrentVisiableState==isVisiable){
//防止调用两次更新,存在当 mCurrentVisiableState=isVisiable;执行前setUserVisibleHint可能被调用两次
return;
}
mCurrentVisiableState=isVisiable;//进行赋值操作
if (isVisiable){
//可见
if (mIsFirstVisiable){
mIsFirstVisiable=false;
onFragmentFirstVisiable();
}
onFragmentResume();
//处理子ViewPager嵌套使用时,子fragment懒加载的实现
dispatChChildVisiableState(true);
}else {
onFragmentPause();
dispatChChildVisiableState(false);
}
}
/**
- 下面抽取三个方法定义成public方法是因为:
- 父类不知道子类要不要处理可见和不可见,因为子类有可能不需要懒加载,此时子类什么也不需要做
- 当需要懒加载时,才需要复写这三个方法。
- 因此这3个方法是非必须实现的方法,不能定义成抽象方法
*/
/**
- 第一次可见时特殊处理
*/
public void onFragmentFirstVisiable() {
Log.e(TAG,“onFragmentFirstVisiable”);
}
/**
- 不可见时处理相关动作 停止数据的加载
*/
public void onFragmentPause() {
Log.e(TAG,“onFragmentPause”);
}
/**
- 表面可见时 加载数据
*/
public void onFragmentResume() {
Log.e(TAG,“onFragmentResume”);
}
/**
- ViewPage嵌套使用时处理子的Fragment懒加载逻辑
- 通过getChildFragmentManager();获取子Fragment
- @param visiable
*/
private void dispatChChildVisiableState(boolean visiable){
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
上面分享的腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
【Android高级架构视频学习资源】
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
[外链图片转存中…(img-5ayWw3y0-1714624494938)]
【Android高级架构视频学习资源】
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!