Android-Jetpack

FragmentStatePagerAdapter与LiveDa

2019-12-26  本文已影响0人  Magic旭

问题描述

  1. LiveData调用了setValue方法,observe也得不到回调。

问题产生条件

  1. 在历史前辈的老代码下,因为需要在页面增加埋点曝光,埋点曝光系统会调用getItem获取当前不可见的fragment,拿到里面接口提供的方法作为上报参数。这时候老前辈们用的SparseArray把fragment的实例存在,根据position去拿里面对应的frament来复用。(可以忽略为什么用SparseArray,原本可能意思就是用下发id作为key的,后面竟然用了position当做key了)。
  2. 开发中用了ViewModel+LiveData实现MVVM模式,LiveData作为数据请求的发生者,observe在Fragment中最为一个订阅者。
  3. PagerAdapter使用的是FragmentStatePagerAdapter(超过limit数量,ViewPager会把fragment给onDetach掉)

问题场景

ViewPagerAdapter
  1. adapter是继承FragmentStatePagerAdapter的。
  2. 可以看到通过List数组里面判断是否有存到对应的实例,如果没有就重新创建,把实例存到数组里面。
class XXXXXlAllPagerAdapter(val context: Context, fm: FragmentManager, private val categoryList: List<XXXXXXX>)
    : FragmentStatePagerAdapter(fm) {
    override fun getItem(position: Int): Fragment? {
        var result: Fragment? = mFragments.get(position)
        if (result == null) {
            result = if (categoryList[position].tab_type == MY_SUBSCRIBE_TYPE) {
                generateMySubscribeFragment(categoryList[position])
            } else {
                generateFragment(categoryList[position])
            }
            mFragments.put(position, result)
        }
        return result
    }
}
fragment里的LiveData(出错的用法)
  1. 用LiveData作为接口请求数据的发送者,在Fragment的onAttach方法注册observe。
    温馨提示:这里ViewModel的of方法建议大家都去看下源码,很简单的,就是把fragment或者Activity作为参数生成HolderFragment,然后将fragment or activity作为key,HolderFragment作为value存在HashMap中。
  2. 我们这里用activity作为of的参数,这样子我的viewMode生命周期将跟随activity的生命周期。
  3. 在onAttach方法注册observe(owner,liveData),然后等待LiveData的回调。当owner状态是DESTROY时候,就不接受回调。(下面再细讲)
class XXXXXXListFragment : BaseXXXXXFragment(){
    private var typeId: Long = 0L
    private var mTabName: String? = null
    private var mAdapter: XXXXXXAdapter? = null
    private var datas: List<XXXXXX>? = null
    private var viewModel: XXXXViewModel? = null

    private val dataObserver: Observer<Resource<List<XXXXX>>> = Observer { res ->
        when (res?.status) {
            Status.LOADING -> {
                ……
            }
            Status.SUCCESS -> {
               ……
            }
            Status.ERROR -> {
               ……
            }
        }
    }

    companion object {
        const val KEY_XXXX_ID = "key_xxxx_id"
        const val KEY_XXXX_NAME = "key_xxxx_name"
        const val INVALID_XXXX_ID = -1L
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ………
        //重建后数据恢复
        if (viewModel != null) {
            datas = viewModel?.getDataOf(typeId)?.value?.data
        }
    }

    override fun setUserVisibleCompat(isVisibleToUser: Boolean) {
        super.setUserVisibleCompat(isVisibleToUser)
        if (datas.isNullOrEmpty() && isVisibleToUser) {
            refresh(typeId)
        }
    }

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        val idStr = arguments?.getString(KEY_XXXX_ID)
        typeId = idStr?.toLongOrNull() ?: INVALID_XXXX_ID
        mTabName = arguments?.getString(KEY_XXXX_NAME)
        activity?.let {
            //activity实例相同情况下,ViewModel返回的实例子也是一样的
            viewModel = ViewModelProviders.of(
                it,
                ViewModelProvider.AndroidViewModelFactory.getInstance(it.application)
            ).get(ChannelAllListViewModel::class.java)
            viewModel?.getDataOf(typeId)?.observe(this, dataObserver)
        }
    }

    override fun onViewCreated(recyclerView: RecyclerView?, savedInstanceState: Bundle?) {
        setupRecyclerView()
    }

  fun refresh(id: Long) {
        if (id == ChannelAllListFragment.INVALID_CHANNEL_ID) {
            //id invalid
            return
        }
        val meta = getMetaData(id)
        val res = getDataOf(id)
        if (meta.loading) return
        //请求接口
        viewModel?.refresh(meta,res)
        ……
    }

    private fun setupRecyclerView() {
        if (mAdapter == null) {
            mAdapter = ChannelAllListAdapter(this)
        }
        datas?.let {
            //Diff计算adapter的差异数据并刷新
            mAdapter?.setData(it)
        }
        ……
    }
}
ViewModel
  1. ViewModel主要做的事情就是数据请求与数据二次处理。这里主要看点在于描述清楚我没有用错liveData的回调方法而已。
class XXXXXViewModel(application: Application) : AndroidViewModel(application) {
    //MutableLiveResource可以自己看下源码就懂了
    val tabList: MutableLiveResource<List<B>> = MutableLiveData()

    private val dataMap: LongSparseArray<MutableLiveResource<out List<B>>> = LongSparseArray()
    private val metaMap: LongSparseArray<A> = LongSparseArray()
    //这里主要通过一个ViewModel把全部fragment的数据都存起来了
    //meta就是接口返回的真实数据,res就是LiveData(一个LiveData对应一个页面的observe)
    private fun refresh(meta: A, res: MutableLiveResource<out List<B>>) {
        ……
        api.getAllChannel(accessKey, meta.typeId, meta.offset)
            .enqueue(object : XXXXApiDataCallback<XXXX>() {
                override fun onDataSuccess(data: XXXXX?) {
                    ……
                    if (items != null && items.isNotEmpty()) {
                        ……
                        meta.list.addAll(items)
                        postData(res, cloneItemList(meta.list))
                    } else {
                        onError(null)
                    }
                }

                override fun onError(t: Throwable?) {
                   ……
                    res.value = Resource.error(t ?: BiliApiException())
                }
            })
    }
    ……
}
问题描述
  1. 当我的页面第一次进来的时候,liveData正常回调,当我的ViewPager切换,相邻页面超过limit,页面A被onDetach后,重新onAttach回来,这时候我viewModel里面的LiveData.setValue后,fragment的observe得不到任何回调。(注意,这时候出错的liveData.observe方法还是放在onAttach上面的)
  2. 问题截图,当fragmentA重新onAttach的时候,第二次打印owner的state是ONDESTROY


    State状态

方案的启蒙者

英文博客,需要翻墙

LifecycleRegistry与LiveData源码解析

LifecycleRegistry
  1. handleLifecycleEvent方法,将Lifecycle.Event转换成State类的对应状态。State的状态在LiveData里面会用到的。
public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        //getStateAfter太简单了,自己去看吧,不粘代码了
       //主要就是
      //ON_CREATE和ON_STOP 对应State的CREATED状态
      //ON_START和ON_PAUSE对应State的STARTED状态
     //ON_RESUME对应State的RESUMED
     //ON_DESTROY对应State的DESTROYED
        State next = getStateAfter(event);
        moveToState(next);
    }
  1. moveToState方法。内部源码主要就是讲observe方法存储起来的Observe是否新旧state状态是否一致,如果不一致就把存储起来的Observe状态全部变成newState。
private void sync() {
      ……
      //isSynced判断是否需要更新状态
        while (!isSynced()) {
            ……
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                //注意这里和下面的方法,会涉及到这次bug出现的原因
                backwardPass(lifecycleOwner);
            }
            ……
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                //注意这里和上面的方法,会涉及到这次bug出现的原因
                forwardPass(lifecycleOwner);
            }
        }
        ……
    }
  1. 这里就以backwardPass方法讲解就可以了。首先遍历Map数组拿到Iterator,然后更新Observe的State状态值。
private void forwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
                mObserverMap.iteratorWithAdditions();
        while (ascendingIterator.hasNext() && !mNewEventOccurred) {
            ……
                //dispatchEvent将当前mState新状态值回调过去,接下来跟踪下dispatchEvent方法
                observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
            ……
            }
        }
    }
LiveData源码
  1. ObserverWithState类的dispatchEvent方法里,会调用GenericLifecycleObserver的onStateChanged方法。这里我们看其中一个子类的处理方式,大致上实现GenericLifecycleObserver接口的子类处理都大同小异。
  2. 在LifecycleBoundObserver我们就能找到LiveData调用了setValue也得不到回调的原因了。
//这里是LiveData里面的一个内部类
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        ……
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            //内部判断当前owner的State状态,如果等于DESTROYED,直接移除了Observer
           //如果我们添加上去也会被系统给移除了
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }
      ……
    }
问题如果产生思考过程
  1. FragmentA重新onAttach上去后,我在onAttach的时候注册了Observer,onStateChanged方法被系统调用时候,就把我的Observer给移除了。
    试错:将注册方法放在onCreate里,还是没效果,因此思考了方法2.
  2. 在启蒙者给的思路,大概是Fragment重新onAttach上去后,内部的State永远都是处于DESTROYED状态,需要开发者手动去改变。
    总结:这就是用FragmentStatePagerAdapter与把fragment实例存下来导致的锅。如果是重新创建实例是没这个问题的。

解决方案

  1. 需要在这个Fragment里面的生命周期内都手动handleLifecycleEvent。
  2. 抽象一个BaseFragment,把生命周期内都手动handleLifecycleEvent的代码写到基类里面,这个基类专门处理这种类型的场景。(个人推荐方法2)
  3. 中间遇到小插曲,也验证了我上面方法1是理解正确的。我刚开始用方案1时候,把LiveData的observe方法放在了handleLifecycleEvent(Lifecycle.Event.ON_CREATE)方法的前面,运行后发现setValue还是失效。突然想到看源码领悟的知识,将observe方法放在handleLifecycleEvent方法后面,就解决了这个bug了。
//抽一个基类,observe要放在super.onCreate之后执行
open class BaseLifecycleFragment : BaseFragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        (lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }

    override fun onStart() {
        super.onStart()
        (lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_START)
    }

    override fun onResume() {
        super.onResume()
        (lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
    }

    override fun onPause() {
        (lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        super.onPause()
    }

    override fun onStop() {
        (lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
        super.onStop()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        (lifecycle as? LifecycleRegistry)?.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    }
}

总结

  1. LiveData的源码可读性强,手机debug查看完美支持,建议大家抽空可以去自己阅读下。
  2. FragmentStatePagerAdapter是用在多个Fragment的场景下的,超过limit要被回收的。如果你在需求上遇到了也需要保持实例的引用,而且遇到了和我一样相同的问题,不妨一起讨论下。
上一篇下一篇

猜你喜欢

热点阅读