添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
非常酷的鼠标垫  ·  GitHub - ...·  昨天    · 
八块腹肌的大葱  ·  Maven Build 중 ...·  1 周前    · 
机灵的皮带  ·  c# ...·  1 年前    · 
欢快的匕首  ·  Make it possible to ...·  1 年前    · 

当面试官提出上述问题时,很多人可能会想到借助 setUserVisiblity() 实现。

如下,当 Fragment 可见时调用 onVisible() 从而实现异步加载。

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
  super.setUserVisibleHint(isVisibleToUser);
  if (getUserVisibleHint()) {
    isVisible = true;
    onVisible();
  } else {
    isVisible = false;
    onInVisible();
 

放在两年前,这个答案是 OK 的,但是 2021 年的今天还这么回答可能就不过关了。

https://android-review.googlesource.com/c/platform/frameworks/support/+/945776

AndroidX 自 1.1.0-alpha07 起, 为 FragmentTransaction 增加了新的方法 setMaxLifeCycle(), 官方建议开发者以此取代 setUserVisibleHint(),这将带来如下好处:

  1. 基于 Lifecycle 的懒加载更加科学,可以配合 Livedata 等组件在 MVVM 架构中使用。

  2. setMaxLifeCycle() 无需额外定义 Fragment 基类,做到无入侵。

一、setMaxLifecycle 实现懒加载

FragmentPagerAdapter 的构造方法中新增了一个 behavior 参数, 当参数设置为 FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 时,就会使用 setMaxLifecycle() 来限制了 Fragment 的生命周期,只有当 Fragment 显示在屏幕中时才会执行 onResume(),这样就可以把加载数据的方法放在 onResume() 中从而实现懒加载。

代码如下:

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
    super.onCreate(savedInstanceState, persistentState)
    setContentView(R.layout.activity_main)
    val viewPager: ViewPager = findViewById(R.id.viewpager)
    val fragmentList: MutableList<Fragment> = ArrayList()
    fragmentList.add(Fragment1())
    fragmentList.add(Fragment2())
    fragmentList.add(Fragment3())
    // 为MyPagerAdapter适配器设置FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 参数
    val myPagerAdapter: MyPagerAdapter = MyPagerAdapter(
      getSupportFragmentManager(),
      FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, fragmentList
    viewPager.setAdapter(myPagerAdapter)
    // 设置预加载为3页,来测试懒加载是否成功
    viewPager.offscreenPageLimit = 3
  class MyPagerAdapter(
    fm: FragmentManager,
    behavior: Int,
    val fragmentList: List<Fragment>
    FragmentPagerAdapter(fm, behavior) {
    override fun getCount() = fragmentList.size
    override fun getItem(position: Int) = fragmentList[position]
 

FragmentPagerAdapter 在创建 Fragment 后,根据 behavior 调用了 setMaxLifecycle()

//FragmentPagerAdapter.java
public FragmentPagerAdapter(@NonNull FragmentManager fm,
        @Behavior int behavior) {
  mFragmentManager = fm;
  mBehavior = behavior;
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
  // ...
  if (fragment != mCurrentPrimaryItem) {
      fragment.setMenuVisibility(false);
      // mBehaviour为1的时候走新逻辑
      if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
          // 初始化item时将其生命周期限制为STARTED
          mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
      } else {
          // 兼容旧版逻辑
          fragment.setUserVisibleHint(false);
  return fragment;
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
  Fragment fragment = (Fragment)object;
  if (fragment != mCurrentPrimaryItem) {
    if (mCurrentPrimaryItem != null) {
      mCurrentPrimaryItem.setMenuVisibility(false);
      if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
        // ...
        // 滑走的会变成非主item, 设置其Lifecycle为STARTED
        mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
      } else {
        mCurrentPrimaryItem.setUserVisibleHint(false);
    fragment.setMenuVisibility(true);
    if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
      // ...
      // 设置新滑到的主item的Lifecycle为RESUMED
      mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
    } else {
      fragment.setUserVisibleHint(true);
    mCurrentPrimaryItem = fragment;
 

通过源码可以知道,即使不借助 behavior,在自定义 Adapter 中构建 Framgent 时直接调用 setMaxLifecycle() 也是等价的。

二、setMaxLifecycle 原理

面试官:是否了解 setMaxLifecycle() 的实现原理?

setMaxLifecycle() 使用起来非常简单,所以为了试探候选人的技术深度,面试官有可能会追问其实现原理。此时,了解背后原理的你便可以在众多候选人中脱颖而出。

接下来,通过源码(基于 1.3.0-rc01)了解一下实现原理。

OP_SET_MAX_LIFECYCLE

我们知道 FramgentTransition 对 Fragment 的所有操作都将转换为一个 Op,针对 setMaxLifecycle() 也同样增加了一个新的 Op -- OP_SET_MAX_LIFECYCLE, 专门用来处理对生命周期的限制。

@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
        @NonNull Lifecycle.State state) {
  addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
  return this;
 

当 FramgentTransition 对某个 Frament 添加了 OP_SET_MAX_LIFECYCLE 后,在其实现类 BackStackRecord 中, FragmentManager 会遍历 Transaction 的 Op 列表,设置 Fragment 的 mMaxState 表明其被允许的最大生命周期。

void executeOps() {
  final int numOps = mOps.size();
  for (int opNum = 0; opNum < numOps; opNum++) {
    final Op op = mOps.get(opNum);
    final Fragment f = op.mFragment;
    //...
    switch (op.mCmd) {
      //...
      // 新引入的这个Op类型, 在这里会给这个Fragment设置允许的生命周期上限
      case OP_SET_MAX_LIFECYCLE:
        mManager.setMaxLifecycle(f, op.mCurrentMaxState);
        break;
      //...
 

mMaxState 的设置是通过在 FragmentManager 中的同名方法 setMaxLifeCycle() 完成的。

void setMaxLifecycle(@NonNull Fragment f, @NonNull Lifecycle.State state) {
  //...
  f.mMaxState = state;
 

FragmentStateManager

调用 setMaxLifecycle() 之后, FragmentManager 会通过 FragmentStateManager 对 Fragment 生命周期做出限制。

值得一提的是,FragmentStateManager 是 1.3.0-alpha08 之后新增的类,将原来和 State 相关的逻辑从 FragmentManager 抽离了出来, 减少了很多与 Fragment 的耦合, 职责更加单一。

接下来看一下,在 FragmentStateManager 中具体是如何限制 Fragment 生命周期的。

void moveToExpectedState() {
  try {
    // 循环计算声明周期是否可以推进
    while ((newState = computeExpectedState()) != mFragment.mState) {
      if (newState > mFragment.mState) {
        // 生命周期向前推进
        int nextStep = mFragment.mState + 1;
        //...
        switch (nextStep) {
          //...
          case Fragment.ACTIVITY_CREATED:
              //...
          case Fragment.STARTED:
              start();
              break;
          //...
          case Fragment.RESUMED:
              resume();
              break;
      } else {
        // 如果应有的生命周期小于当前, 后退
        int nextStep = mFragment.mState - 1;
        //...
        switch (nextStep) {
         // 与上面的switch类似
         //...
    // ...
  // ...
 
int computeExpectedState() {
  // 其他计算expected state的逻辑, 算出maxState
  //...
  // mMaxState 对生命周期做出限制
  switch (mFragment.mMaxState) {
    case RESUMED:
        break;
    case STARTED:
        maxState = Math.min(maxState, Fragment.STARTED);
        break;
    case CREATED:
        maxState = Math.min(maxState, Fragment.CREATED);
        break;
    default:
        maxState = Math.min(maxState, Fragment.INITIALIZING);
  // 其他计算expected state的逻辑, 算出 maxState
  // ...
  return maxState;
 

整体流程图如下:

除了使用默认的 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,我们甚至可以在自定义 Adapter 的 instantiateItem 中为将 Fragment 的 MaxLifecycle 设置为 CREATED, 这样可以让 Fragment 只走到 onCreate() 从而延迟更多操作, 比如在 onCreateView() 中的 inflate 以及 onViewCreated() 中的一些操作。

Tips:Fragment 1.3.0-rc01 已经支持设置最大生命周期为 INITIALIZED。

-- End --

Android启动优化:合并三方SDK的多个FileProvider

Flutter的const关键字,加载Widget前代表什么?

RecyclerView item 动画,案例+讲解!

本文对你有帮助吗?留言、转发、点好看是最大的支持,谢谢!

面试官:ViewPager 中的 Fragment 如何实现懒加载?当面试官提出上述问题时,很多人可能会想到借助 setUserVisiblity() 实现。如下,当 Fragment 可... 所谓懒加载,就是当fragment完全可见的时候我们再去加载数据,我们在做应用开发的时候,一个Activity里面可能会以viewpager(或其他容器)与多个Fragment来组合使用,而如果每个fragment都需要去加载数据,或从本地加载,或从网络加载,那么在这个activity刚创建的时候就变成需要初始化大量资源。这样的结果,我们当然不会满意。那么,能不能做到当切换到这个fragment的时候,它才去初始化呢? 答案就在Fragment里的setUserVisibleHint这个方法里。 请看关于Fragment里这个方法的API文档: Set a hint to the sy
为什么使用setMaxLifecycle setUserVisibleHint方式已经不推荐使用了,替代它的方案就是setMaxLifecycle,这里不再具体讲解setMaxLifecycle(讲我也不会哈哈) 推荐一篇文章:setMaxLifecycle讲解,想具体了解可以看看。 实现fragment懒加载FragmentPagerAdapter适配器构造方...
androidx中**Fragment#setUserVisibleHint(boolean)方法已经被淘汰了,取而代之的是FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)**方法,该方法可以直接干预Fragment生命周期执行。 FragmentTransaction中方法setMaxLifecycle实现源码如下: @NonNull public FragmentTransaction setMaxLifecyc
本文的分析基于androidx 1.1.0版本,文中提到的setMaxLifecycle()方法是1.1.0-alpha07版本才引入的。最近把Android Studio更到了3.5版本,建项目时发现竟然已经强制使用androidx包了。 于是想着把以前项目中的一些公共类,像BaseActivity、BaseFragment等等都迁移到androidx方便今后的开发,要做的也很简单,就是重导包,将原来的“support系列”替换为“androidx系列”。简单的迁移完成后我发现此前实现懒加载Fra
一开始不知道这个方法,用到tablayout+ viewpager,viewpager里面包含的是fragment,有这样的需求就是,每次滑动需要刷当前页面,百度一看,有两个方法可以实现,1.setUserVisibleHint 2.onHiddenChanged 如果使用的是上面这张viewpager+tablayout 这种方法,用setUserVisibleHint可以有效的实现刷数据...
ViewPager懒加载请注意 版的Fragment中(Version 1.1.0-alpha07),该方法setUserVisibleHint已经过时,由FragmentTransactionsetMaxLifecycle替代,版本的FragmentPagerAdapter可以设置直接调用生命周期,这代表ViewPager+Fragment懒加载有更好的解决方案,请注意 Adapter小结...
AndroidStudio里的解释是这样子的: androidx.fragment.app.FragmentStatePagerAdapter public FragmentStatePagerAdapter(@NonNull androidx.fragment.app.FragmentManager fm, int behavior) Constructor for FragmentStatePagerAdapter. If BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
大家都知道viewpager默认会加载下一页数据,viewpager提供了一个方法setOffscreenPageLimit(),设置预加载页数 懒加载字面意思当需要的时候才去加载,不需要时不加载 之前处理fragment懒加载通过setUserVisibleHint + onHiddenChanged这2个函数(详情此处省略),而在Androidx模式下此方法被弃用了 ,用FragmentTransaction.setMaxLifecycle()来代替 Google在Androidx下增加了se..
引用和提到了androidx中实现Fragment懒加载的方案。在androidx中,可以使用setMaxLifecycle()方法来设置Fragment的状态,取代了setUserVisibleHint()方法。通过在构造FragmentPagerAdapter时传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT参数,并将加载数据的逻辑放到Fragment的onResume()方法中,就可以实现Fragment懒加载。这种方案可以有效地减少不必要的数据加载和提高性能。同时,androidx还引入了一些的内容,为开发者提供了很多便利。你可以参考相关的文章和代码来深入了解和实践该方案。 引用提到了网上已经有很多关于androidxFragment懒加载的文章,但大多数只是点到了setMaxLifecycle()和修改FragmentPagerAdapter这两个方面,很少有实践的文章。因此,该文章详细记录了作者实践后的结果,可以作为更加详尽的参考。 总结起来,androidx中的Fragment懒加载可以通过setMaxLifecycle()方法和修改FragmentPagerAdapter来实现,并且androidx还引入了一些的内容,为开发者提供了更多便利。你可以参考相关的文章和代码来了解和实践该方案。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [androidx中的Fragment懒加载方案](https://blog.csdn.net/qq_36486247/article/details/102531304)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [androidx中的Fragment懒加载](https://blog.csdn.net/tongsiw/article/details/107991830)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [androidx下的fragment的lazy懒加载问题详解](https://download.csdn.net/download/weixin_38606656/14916018)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]