0%

LazyLoad


最近公司的项目做性能优化,正在折腾Fragment懒加载的东西,由于项目迁移到了AndroidX,懒加载方案和以前有所不同,不过总体来说代码量少了很多,正好可以对新老版本的懒加载方案做一个对比,下面是关于AndroidX前和AndroidX后的fragment懒加载方案总结


AndroidX之前采用旧懒加载方案

4步优化

  1. View已加载且fragment可见时懒加载
  • 当onViewCreated()方法执行时,表明View已经加载完毕,isViewCreated标记为true,并调lazyLoad()方法
  • 当setUserVisibleHint(boolean isVisibleToUser)执行时,isVisibleToUser为true并调lazyLoad()方法
  • 在lazyLoad()方法双重标记判断,再进行停止一切/加载数据事件分发
  • 定义抽象方法loadData(),子类重写进行加载数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    private boolean isViewCreated = false;
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    ...
    isViewCreated = true; // View已加载
    // 此时正好可见
    if (getUserVisibleHint()) {
    setUserVisibleHint(true);
    }

    return rootView;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    // View已加载
    if (isViewCreated) {
    // 根据当前可见状态分发事件
    if (isVisibleToUser) {
    dispatchUserVisibleHint(true);
    } else {
    dispatchUserVisibleHint(false);
    }
    }
    }

    private void dispatchUserVisibleHint(boolean visibleState) {
    if (visibleState) {
    // 加载网络数据请求
    onFragmentLoad();
    } else {
    // 停止网络数据请求
    onFragmentLoadStop();
    }
    }

    public void onFragmentLoadStop() {
    E("onFragmentLoadStop");
    }

    public void onFragmentLoad() {
    E("onFragmentLoad");
    }
  1. 记录上一次状态与当前状态比较,保证变化过程,状态未改变不需要分发事件
  • 从可见到不可见,停止一切操作
  • 从不可见到可见,加载数据操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    private boolean isVisibleStateUP = false; // 记录上一次可见的状态
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isViewCreated) {
    // 当前可见且上一次不可见
    if (isVisibleToUser && !isVisibleStateUP) {
    dispatchUserVisibleHint(true);
    }
    // 当前不可见且上一次可见
    else if (!isVisibleToUser && isVisibleStateUP){
    dispatchUserVisibleHint(false);
    }

    }
    }

    private void dispatchUserVisibleHint(boolean visibleState) {
    this.isVisibleStateUP = visibleState;
    if (visibleState) {
    // 加载网络数据请求
    onFragmentLoad();
    } else {
    // 停止网络数据请求
    onFragmentLoadStop();
    }
    }
  1. 启动新的Activity没有分发事件
  • 除了onCreate和setUserVisibleHint在onResume/onPause中也要分发事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Override
    public void onResume() {
    super.onResume();
    // 当前可见且上一次不可见
    if (getUserVisibleHint() && !isVisibleStateUP) {
    dispatchUserVisibleHint(true);
    }
    }

    @Override
    public void onPause() {
    super.onPause();
    // 当前不可见且上一次可见
    if (getUserVisibleHint() && isVisibleStateUP) {
    dispatchUserVisibleHint(false);
    }
    }
  1. 双重嵌套下子Fragment无法接收到事件
    父fragment不可见直接返回,判断当前fragment是否可见,手动遍历分发每个子fragment停止一切操作/加载数据事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    // 判断父fragment是否可见 
    private boolean isParentInvisible() {
    // 获取父fragment
    Fragment parentFragment = getParentFragment();
    // 父fragment是懒加载fragment的实例
    if (parentFragment instanceof LazyFragment) {
    LazyFragment fragment = (LazyFragment) parentFragment;
    // 父fragment不可见
    return !fragment.isVisibleStateUP;
    }
    return false;
    }

    private void dispatchUserVisibleHint(boolean visibleState) {
    this.isVisibleStateUP = visibleState;
    // 当前fragment可见且父fragment不可见,不分发事件
    if (visibleState && isParentInvisible()) {
    return;
    }

    if (visibleState) {
    onFragmentLoad();
    // 分发子fragment可见事件
    dispatchChildVisibleState(true);

    } else {
    onFragmentLoadStop();
    // 分发子fragment不可见事件
    dispatchChildVisibleState(false);
    }
    }

    protected void dispatchChildVisibleState(boolean state) {
    FragmentManager fragmentManager = getChildFragmentManager();
    List<Fragment> fragments = fragmentManager.getFragments();
    if (fragments != null) {
    // 遍历子Fragment分发事件
    for (Fragment fragment: fragments) {
    // fragment是懒加载实例未被隐藏且可见
    if (fragment instanceof LazyFragment &&
    !fragment.isHidden() &&
    fragment.getUserVisibleHint()) {
    ((LazyFragment5)fragment).dispatchUserVisibleHint(state);
    }
    }
    }
    }

    AndroidX之后采用新懒加载方案

    Google在Androidx在FragmentTransaction中增加了setMaxLifecycle方法控制Fragment 调用的最大的生命周期函数。该方法可以设置活跃状态下Fragment最大状态,如果该Fragment 超过了设置的最大状态,会强制将Fragment降级到正确的状态

    viewPager下设置adapter的behavior

    BEHAVIOR_SET_USER_VISIBLE_HINT:Fragment对用户可见状态发生改变时,setUserVisibleHint方法会被调用。
    BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT:当前选中的Fragment在Lifecycle.State#RESUMED状态,其他不可见的 Fragment限制在Lifecycle.State#STARTED状态
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    open class FragmentLazyPagerAdapter(
    fragmentManager: FragmentManager,
    private val fragments: MutableList<Fragment>,
    private val titles: MutableList<String>
    ) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

    override fun getItem(position: Int) = fragments[position]

    override fun getCount() = fragments.size

    override fun getPageTitle(position: Int) = titles[position]

    }

    封装懒加载fragment

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    abstract class LazyFragment : Fragment() {

    // 是否加载过标记
    private var isLoaded = false

    override fun onResume() {
    super.onResume()
    if (!isLoaded) {
    lazyInit()
    Log.d(TAG, "lazyInit:!!!!!!!")
    isLoaded = true
    }
    }

    override fun onDestroyView() {
    super.onDestroyView()
    isLoaded = false
    }

    abstract fun lazyInit()
    }

    add/show/hide时设置最大生命周期(未使用viewPager)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // 初始化fragment
    private fun loadFragmentsTransaction(
    @IdRes containerViewId: Int,
    showPosition: Int,
    fragmentManager: FragmentManager,
    vararg fragments: Fragment
    ) {
    if (fragments.isNotEmpty()) {
    fragmentManager.beginTransaction().apply {
    for (index in fragments.indices) {
    val fragment = fragments[index]
    add(containerViewId, fragment, fragment.javaClass.name)
    if (showPosition == index) {
    setMaxLifecycle(fragment, Lifecycle.State.RESUMED)
    } else {
    hide(fragment)
    setMaxLifecycle(fragment, Lifecycle.State.STARTED)
    }
    }

    }.commit()
    } else {
    throw IllegalStateException(
    "fragments must not empty"
    )
    }
    }

    // 展示/隐藏fragment
    private fun showHideFragmentTransaction(fragmentManager: FragmentManager, showFragment: Fragment) {
    fragmentManager.beginTransaction().apply {
    show(showFragment)
    setMaxLifecycle(showFragment, Lifecycle.State.RESUMED)

    //获取其中所有的fragment,其他的fragment进行隐藏
    val fragments = fragmentManager.fragments
    for (fragment in fragments) {
    if (fragment != showFragment) {
    hide(fragment)
    setMaxLifecycle(fragment, Lifecycle.State.STARTED)
    }
    }
    }.commit()
    }

    Fragment嵌套下的问题

    第一次初始化时,同级不可见的Fragment仍然要调生命周期方法,需增加Fragment是否可见的判断
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    abstract class LazyFragment : Fragment() {

    private var isLoaded = false

    override fun onResume() {
    super.onResume()
    //增加Fragment是否可见的判断
    if (!isLoaded && !isHidden) {
    lazyInit()
    Log.d(TAG, "lazyInit:!!!!!!!")
    isLoaded = true
    }
    }

    override fun onDestroyView() {
    super.onDestroyView()
    isLoaded = false
    }

    abstract fun lazyInit()

    }

    ViewPager2懒加载

    最新的ViewPager2默认就实现了懒加载,可以说不用任何处理

但是ViewPager2中的RecyclerView可以缓存Fragment的数量是有限的,会造成Fragment的多次销毁和创建,也可通过setOffscreenPageLimit()方法设置预加载数量,再用AndroidX下的懒加载fragment方式去处理