看到运维APP主页的代码时,发现在ViewPager+Fragment+TabLayout中使用到的FragmentPagerAdapter构造函数被提示弃用了,如下:

//自定义一个FragmentPagerAdapter,提示被弃用
inner class OperationManageFragmentPagerAdapter( fm: FragmentManager) :
        FragmentPagerAdapter(fm){
        private val mTitles:ArrayList<String> = getTitles()
        private fun getTitles(): ArrayList<String>{
        override fun getItem(position: Int): Fragment {
            return fragments[position]
        override fun getCount(): Int {
            return fragments.size
        override fun getPageTitle(position: Int): CharSequence {
            return mTitles[position]

直接看一下该构造函数的注释:

* Constructor for {@link FragmentPagerAdapter} that sets the fragment manager for the adapter. * This is the equivalent of calling {@link #FragmentPagerAdapter(FragmentManager, int)} and * passing in {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}. * <p>Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the * current Fragment changes.</p> * @param fm fragment manager that will interact with this adapter * @deprecated use {@link #FragmentPagerAdapter(FragmentManager, int)} with * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} //单参数的构造函数提示被废弃 @Deprecated public FragmentPagerAdapter(@NonNull FragmentManager fm) { this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);

这里是建议使用双参数的构造函数来使用,需要传递一个BEHAVIOR,而且默认单参数的传递是 BEHAVIOR_SET_USER_VISIBLE_HINT值,当然还有一个值是BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,这2个有什么用呢,为何要弃用一个。

直接看2个值的解释:

* Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current * fragment changes. * @deprecated This behavior relies on the deprecated * {@link Fragment#setUserVisibleHint(boolean)} API. Use * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement, * {@link FragmentTransaction#setMaxLifecycle}. * @see #FragmentPagerAdapter(FragmentManager, int) @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
  • 当前API版本已经被废弃。
  • 当使用这个策略时,fragment在切换时会回调setUserVisibleHint()方法。
  • * Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED} * state. All other Fragments are capped at {@link Lifecycle.State#STARTED}. * @see #FragmentPagerAdapter(FragmentManager, int) public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
  • 指明在切换fragment时,当前fragment会回调生命周期到RESUMED,其他fragment会回调生命周期到STARTED。
  • 不会回调setUserVisibleHint()方法。
  • 可以手动调用setMaxLifecycle来设置fragment生命周期最高级别。
  • 接着看一下源码中的调用过程,在ViewPager切换时会调用下面代码:

    mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
    

    通过Adapter来切换fragment,然后在FragmentPagerAdapter类中该函数的实现:

    //FragmentPagerAdapter实现该函数
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        //需要切换的fragment
        Fragment fragment = (Fragment)object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                //处理当前在前台的fragment,退到后台
                mCurrentPrimaryItem.setMenuVisibility(false);
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    //开始事务
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    //设置生命周期回调
                    mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                } else {
                    //默认调用setUserVisibleHint函数
                    mCurrentPrimaryItem.setUserVisibleHint(false);
            fragment.setMenuVisibility(true);
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                if (mCurTransaction == null) {
                    mCurTransaction = mFragmentManager.beginTransaction();
                //即将显示的Fragment设置为RESUMED
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
                //之前方法,调用setUserVisibleHint函数
                fragment.setUserVisibleHint(true);
            mCurrentPrimaryItem = fragment;
    
  • 对旧的fragment,如果是BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT模式时,直接设置其生命周期为STARTED,否则回调setUserVisibleHint(false)。
  • 对于即将显示的fragment,如果是BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT模式时直接设置周期为RESUMED,否则回调setUserVisibleHint(true)。
  • 在项目ItemListFragment中添加以下测试代码:

        //测试代码
        override fun setUserVisibleHint(isVisibleToUser: Boolean) {
            Log.i(TAG, "setUserVisibleHint: type = $mQueryType isVisibleToUser = $isVisibleToUser")
        override fun onResume() {
            super.onResume()
            Log.i(TAG, "onResume: type = $mQueryType")
        override fun onStart() {
            super.onStart()
            Log.i(TAG, "onStart: type = $mQueryType")
    

    使用单参数的构造函数结果是:

    会发现生命周期不论是显示出来的界面还是没有显示出来的界面都回调了onResume和onRestart函数。 从待处理Fragment切换到已处理Fragment再切换到全部Fragment:

    生命周期回调将不再回调。 先回调不可见fragment的setUserVisibleHint函数,再回调即将可见的。

    使用推荐的没有废弃的构造函数:

    打印结果是:

    不再回调setUserVisibleHint函数 只有可见的才回调onResume方法,否则只回调onStart方法。 从待处理切换到已处理再切换到全部:

    不论是哪种方式实现的fragment切换,都只有一个目的,那就是对用户可见性的判断,在这个之上我们最常见的场景:

  • 懒加载,当fragment内容很多时,由于ViewPager的缓存特性会缓存创建左右至少2个fragment实例,这时如果在实例创建时就进行网络加载会造成大量请求同时并发的现象,影响当前可见的fragment内容显示,所以合理的当fragment可见时再进行网络请求加载会好很多。
  • 后台服务,当fragment在不可见时需要停止一些服务,比如切换走地图页停止定位服务,可以节省APP内存消耗,提高性能。
  • 在之前单参数时的打印有个问题

    也就是这里的type都是all,而且都是false,明明是有一个fragment是可见的,为了解决这个问题,查看一下源码发现有2个地方会回调这个方法:

    一个是fragment实例化的时候,一个是设置该fragment的时候,也就是说fragment被创建时,必然会回调setUserVisibleHint方法,然后再在setPrimaryItem中又回调一次为ture的。 之前打印没有可能是因为打印问题,后面进行debug,降低程序运行速度,便会发现有4次回调:

    ,至于为什么都是all,是因为解析type的回调在回调之后,显示的是默认值。

    分类:
    Android
    标签: