**最近仔细看了一下作者提交的历史记录,最新版本(先于本文发表时间)已经解决了该问题,解决思路和本文一致"
先说解决方案吧:
修改FragmentMagician的getActiveFragments方法,为:
public static List<Fragment> getActiveFragments(FragmentManager fragmentManager) {
if (!(fragmentManager instanceof FragmentManagerImpl))
return Collections.EMPTY_LIST;
return fragmentManager.getFragments();
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在项目开发中,之前一直指导fragment生命周期可见性超级复杂,项目紧,任务重,没有太多的时间来解决生命周期可见性的逻辑问题,这也是我一直不使用fagment的原因,新的项目因为需要每个页面都显示设备的状态信息,本来想用BaseActivity监听event 事件完成设备的状态监测, 然后在子activity里填充页面内容.但是总感觉不是很爽,早就关注了fragmentation这个开源库,看起来能把fragment的使用超级简化,就想着尝试用一下单activity + fragment 方案.首先不得不感谢一下作者,这个库真的是极大的简化了fragment 的使用
但是在使用过程中,也不知道是我带哦用的问题还是什么原因,出现了onSupportInvisbile 方法不调用的问题,先说一下我的项目跳转顺序

如图四个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();
}
调整后的日志,符合预期,看栈的顺序也符合预期了..出栈正常,有什么其他问题,暂时还没有测试,目前满足这种简单的但是需要可见性检测的场景