Fragmentation onSupportInviable
先声明一下,这个问题有可能是我这边调用的问题,因为之前好像是正常的,不知道为什么出现了本文提到的问题,请各位大神指点一下.本人修改的源码属于头痛治头系列,没有经过严格测试会不会引发其他问题,所以慎重考虑我的方案
在项目开发中,之前一直指导fragment生命周期可见性超级复杂,项目紧,任务重,没有太多的时间来解决生命周期可见性的逻辑问题,这也是我一直不使用fagment的原因,新的项目因为需要每个页面都显示设备的状态信息,本来想用BaseActivity监听event 事件完成设备的状态监测, 然后在子activity里填充页面内容.但是总感觉不是很爽,早就关注了fragmentation这个开源库,看起来能把fragment的使用超级简化,就想着尝试用一下单activity + fragment 方案.首先不得不感谢一下作者,这个库真的是极大的简化了fragment 的使用
但是在使用过程中,也不知道是我带哦用的问题还是什么原因,出现了onSupportInvisbile 方法不调用的问题,先说一下我的项目跳转顺序
image.png如图四个fragment顺序跳转,调用的方法是start(Fragment)
,我的起往是 跳转到下一个fragment 前,当前fragment 知道自己被隐层了,也就是会调用 onSupportInVisble
方法,但是结果却是这样
WaitFragment}=====>onSupportVisible
WaitFragment}=====>onSupportInVisible //注意一下只有第一个调用了 onSupportInvisible
InputCardNoFragment}=====>onSupportVisible
BeforeTakePhotoFragment}=====>onSupportVisible
TakePhotoFragment}=====>onSupportVisible
没办法,看源码把,之前我们先注意一下,第一个也就是WaitFragment有掉onSupportInVisible方法,其他都没调
一系列的调用就不多罗嗦了,我们直接跳转到关键方法,为TransactionDelegate
类的doDispatchStartTransaction(FragmentManager fm, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type)
方法,from
是调用start
方法的fragmnet,to
是要跳转的fragment,我把这个方法的代码跟start方法相关的代码摘了一下主要就两行
private void doDispatchStartTransaction(FragmentManager fm, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type) {
checkNotNull(to, "toFragment == null");
//获取最初的fragment
from = getTopFragmentForStart(from, fm);
/** 一段代码表示从 当前栈顶的fragment 里拿到 containerId把toFragment填进去**/
//开始处理start 的逻辑
start(fm, from, to, toFragmentTag, dontAddToBackStack, sharedElementList, false, type);
}
然后再看start的逻辑
private void start(FragmentManager fm, final ISupportFragment from, ISupportFragment to, String toFragmentTag,
boolean dontAddToBackStack, ArrayList<TransactionRecord.SharedElement> sharedElementList, boolean allowRootFragmentAnim, int type) {
if (addMode) {
ft.add(from.getSupportDelegate().mContainerId, toF, toFragmentTag);
if (type != TYPE_ADD_WITHOUT_HIDE && type != TYPE_ADD_RESULT_WITHOUT_HIDE) {
//这里我们本来想hide的应该是调用start 方法的fragment,也就是上一个方法的from,但是在上一个方法中被替换成了第一个fragment,hide fragment 会调用被hide的fragment 的onSupportInVisible方法(该fragment 得是 SupportFragment的子类)
ft.hide(fromF);
}
supportCommit(fm, ft);
}
大致意思就是在第二个方法中,会hide 掉from
Fragment,但是在第一个方法中,传给第二个方法的from
被替换成了第一个fragment(WaitFragment),每次hide 的也都是都是第一个frgment,这样也跟我们的日志是符合的,即只有第一frgment 会调用onSupoortInVisible
方法.
所以我们现在可以推测,之所以当前fragment 跳转到下一个fragment之前不能调onSupportInviaible
,原因是当前fragment没有按照预想的顺序位于栈顶
这个时候我们在查看栈视图,果然发现这些fragment在栈内的顺序是乱七八糟的,很难找到规律..
那接下来就是要排查为什么当前fragment不能位于栈顶呢,我们继续查看获取栈顶frgment 的方法:getTopFragmentForStart
private ISupportFragment getTopFragmentForStart(ISupportFragment from, FragmentManager fm) {
ISupportFragment top;
if (from == null) {
top = SupportHelper.getTopFragment(fm);
} else {
if (from.getSupportDelegate().mContainerId == 0) {
Fragment fromF = (Fragment) from;
if (fromF.getTag() != null && !fromF.getTag().startsWith("android:switcher:")) {
throw new IllegalStateException("Can't find container, please call loadRootFragment() first!");
}
}
top = SupportHelper.getTopFragment(fm, from.getSupportDelegate().mContainerId);
}
return top;
}
核心代码就是
SupportHelper.getTopFragment(fm, from.getSupportDelegate().mContainerId)
再进去看这个方法:
public static ISupportFragment getTopFragment(FragmentManager fragmentManager, int containerId) {
List<Fragment> fragmentList = FragmentationMagician.getActiveFragments(fragmentManager);
if (fragmentList == null) return null;
for (int i = fragmentList.size() - 1; i >= 0; i--) {
Fragment fragment = fragmentList.get(i);
if (fragment instanceof ISupportFragment) {
ISupportFragment iFragment = (ISupportFragment) fragment;
if (containerId == 0) return iFragment;
if (containerId == iFragment.getSupportDelegate().mContainerId) {
return iFragment;
}
}
}
return null;
}
意思大致就是从FragmentationMagician.getActiveFragments(fragmentManager)
中获取到最末尾的装在containerId中的 SupportFragment,本例四个fragment 全中.
再看getActiveFragments 的源码:
if (!(fragmentManager instanceof FragmentManagerImpl))
return Collections.EMPTY_LIST;
// For pre-25.4.0
if (sSupportLessThan25dot4) return fragmentManager.getFragments();
// For compat 25.4.0+
try {
FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
// Since v4-25.4.0,mActive: ArrayList -> SparseArray
return getActiveList(fragmentManagerImpl.mActive);
} catch (Exception e) {
e.printStackTrace();
}
return fragmentManager.getFragments();
support 版本0超过25.4.0,会走return getActiveList(fragmentManagerImpl.mActive);
这个方法,
private static List<Fragment> getActiveList(HashMap<String, Fragment> active) {
if (active == null) {
return Collections.EMPTY_LIST;
}
final int count = active.size();
ArrayList<Fragment> fragments = new ArrayList<>(count);
fragments.addAll(active.values());
return fragments;
}
意思就是将参数的值封装到list中,之前调用时传入的参数是fragmentManagerImpl.mActive
点进去一看,阿西吧!!!原来是个HashMap
,这顺序怎么保证!!!自然也就乱套了..但是令人感到奇怪的是,点返回键出栈的顺序居然和我们设想的一样,也没再研究作者是怎么做到的.
final ArrayList<Fragment> mAdded = new ArrayList<>();
final HashMap<String, Fragment> mActive = new HashMap<>();
至于这个mActive 和mAdd的fragment 的区别,我看了半天也没搞明白,但是至少madded 是有顺序的,暂时先返回这个吧...有什么隐患以后再说把..于是修改FragmentMagician
的getActiveFragments
方法,强行返回那个有顺序的list:
public static List<Fragment> getActiveFragments(FragmentManager fragmentManager) {
if (!(fragmentManager instanceof FragmentManagerImpl))
return Collections.EMPTY_LIST;
return fragmentManager.getFragments();
}
调整后的日志,符合预期,看栈的顺序也符合预期了..出栈正常,有什么其他问题,暂时还没有测试,目前满足这种简单的但是需要可见性检测的场景