一、Fragmentation是什么?
Fragmentation是一款以解决多Activity+多Fragment或单Activity+多Fragment复杂嵌套问题,快速开发出基于Fragment的app的框架。包括状态保存恢复、生命周期监听、转场动画、启动模式、滑动返回、懒加载以及startForResult等功能。
以下是原文介绍:
为"单Activity + 多Fragment的架构","多模块Activity + 多Fragment的架构"而生,帮你简化使用过程,轻松解决各种复杂嵌套等问题,修复了官方Fragment库存在的一些BUG。
原文地址:
Fragment之我的解决方案:Fragmentation - 。
本文将着重介绍其中Fragment启动模式功能、startForResult、懒加载以及回退功能。
之所以介绍该框架,主要在实际工作中项目的架构也是采用类似Fragment自定义View构成的UI框架形式如单Activity+多Fragment,只是这里Fragment是自定义View,其中堆栈跳转有值得优化的部分,而Fragmentation给了我一些启发,所以本文重点介绍上述功能。
二、启动模式。
Activity启动模式分为四种:
1.standard:
每次启动都会创建一个新的实例,无论这个实例是否已经存在,在这种模式下谁启动了Activity A,那这个A就会运行在启动它的那个Activity所在的任务栈里。(图片引用:https://www.cnblogs.com/claireyuancy/p/7387696.html)
2.singleTop
和standard模式类似,不会创建新的task,都是在原任务栈中创建,也就是该Activity实例在当前栈顶,那么不会创建新的实例,并调用onNewIntent方法。但是如果存在该实例却不在栈顶或不存在该实例都会新建一个新的实例出来。
3.singleTask
singleTask相对比较复杂,先介绍最简单的情形:
在任务栈T3里,存在ADBC三个Activity,这个时候如果启动D,且所需任务栈是T3,根据栈内复用原则D不会重新创建,而是将D之上的Activity弹出栈,将D置为栈顶,表示为AD,B,C被弹出。(图片来源:http://www.jianshu.com/p/2a9fcf3c11e4)
再比如如果启动D,且所需堆栈是T2,由于D和T2都不存在,系统会创建T2和D,并将D压入T2.
还有一种情况启动D且所需堆栈T1,由于D不存在则创建新的实例并压入T1.
相对复杂的情形:
前后台任务栈,前台任务栈包含AB两个activity实例,后台任务栈包含CD两个activity,如果这个时候前台任务栈启动D那么整个后台任务栈都会放到前台,前台的堆栈顺序表示为ABCD,D在顶层。
如果启动的是C,那么前台堆栈顺序为ABC,D被直接出栈了。
最后一种情况,A,B两个应用,C是B的一个Activity,且C的allowtaskReparening属性为true,那么当A应用启动B应用的C时,就会将C放在B的任务栈顶层,再描述的清楚一些就是,启动C以后按home键回到桌面,再点击B应用图标,这个时候展示的不是B应用的主Activity而是C。
4singleinstance。
就是加强版singleTask模式,使用该模式就是启动一个新的任务栈,且这个任务栈里只能存在一个Activity实例。
那么了解了Activity的启动模式以后我们再来看Fragmentation是如何实现Fragment的启动模式的。
Fragmentation只实现了singleTop和singleTask,我们来关注下他是如何实现的。
FragmentManager管理堆栈原理
2.1Fragmentation singleTop模式
@Override
public void start(finalSupportFragment toFragment,@LaunchModefinal int launchMode) {
mFragmentationDelegate.dispatchStartTransaction(getFragmentManager(), this,toFragment,0,launchMode,FragmentationDelegate.TYPE_ADD);
}
首先在supportFragment(备注:基础库中的需要被继承实现的基类)中实现了start方法用于启动新的fragment,内部实现是由FragmentationDelegate代理实现,进一步看:
if(handleLaunchMode(fragmentManager,to,toFragmentTag,launchMode))return;
在dispatchStartTransaction方法中有这么一段代码,用于处理启动模式的,我们再进一步进去看
SupportFragment topFragment = getTopFragment(fragmentManager);//找到栈顶Fragment
Fragment stackToFragment = findStackFragment(toFragment.getClass(),toFragmentTag,fragmentManager);//再从堆栈中找到toFragment
if(launchMode == SupportFragment.SINGLETOP) {
// 在栈顶
if(toFragment == topFragment || toFragment.getClass().getName().equals(topFragment.getClass().getName())) {
handleNewBundle(toFragment,stackToFragment);
return true;
}
}
可以看到当从堆栈中找到所要跳转的toFragment以后且toFragment也是TopFragment那么就会开始处理newBundle,并在handleNewBundle中使用stackToFragment调用onNewBundle方法
private void handleNewBundle(SupportFragment toFragment,Fragment stackToFragment) {
Bundle argsNewBundle = toFragment.getNewBundle();
Bundle args = toFragment.getArguments();
if(args.containsKey(FRAGMENTATION_ARG_CONTAINER)) {
args.remove(FRAGMENTATION_ARG_CONTAINER);
}
if(argsNewBundle !=null) {
args.putAll(argsNewBundle);
}
((SupportFragment) stackToFragment).onNewBundle(args);
}
2.2 Fragmentation singTask模式
在handleLanuchMode方法执行以前都一样,我们来看该方法完整部分
private boolean handleLaunchMode(FragmentManager fragmentManager,SupportFragment toFragment,String toFragmentTag, intlaunchMode) {
SupportFragment topFragment = getTopFragment(fragmentManager);
if(topFragment ==null)return false;
Fragment stackToFragment = findStackFragment(toFragment.getClass(),toFragmentTag,fragmentManager);
if(stackToFragment ==null)return false;
if(launchMode == SupportFragment.SINGLETOP) {
// 在栈顶
if(toFragment == topFragment || toFragment.getClass().getName().equals(topFragment.getClass().getName())) {
handleNewBundle(toFragment,stackToFragment);
return true;
}
}else if(launchMode == SupportFragment.SINGLETASK) {
popToFix(toFragmentTag,0,fragmentManager);//这里是singleTask方法执行关键
handleNewBundle(toFragment,stackToFragment);
return true;
}
return false;
}
它与singleTop不同的地方在于会执行popstack操作。具体我们来看popToFix方法。
private void popToFix(String fragmentTag, intflag, finalFragmentManager fragmentManager) {
if(fragmentManager.getFragments() ==null)return;
mActivity.preparePopMultiple();
fragmentManager.popBackStackImmediate(fragmentTag,flag);//执行回退栈立即执行
mActivity.popFinish();
mHandler.post(newRunnable() {
@Override
public voidrun() {
FragmentTransactionBugFixHack.reorderIndices(fragmentManager);
}
});
}
当执行完pop操作以后,后续的handleNewBundle方法操作一致。
2.3 Fragmentation startForResult
使用startForResult启动Fragment时会在dispatchStartTransaction存储请求requestcode,用于回调
/**
* save requestCode
*/
private void saveRequestCode(Fragment to, int requestCode) {
Bundle bundle = to.getArguments();
if(bundle ==null) {
bundle =newBundle();
to.setArguments(bundle);
}
ResultRecord resultRecord =newResultRecord();
resultRecord.requestCode= requestCode;
bundle.putParcelable(FRAGMENTATION_ARG_RESULT_RECORD,resultRecord);
}
启动后在需要返回的响应码的fragment里存储返回数据并返回resultcode,举个例子:
mBtnModify.setOnClickListener(newView.OnClickListener() {
@Override
public voidonClick(View v) {
Bundle bundle =newBundle();
bundle.putString(DetailFragment.KEY_RESULT_TITLE,mEtModiyTitle.getText().toString());
setFragmentResult(RESULT_OK,bundle);
Toast.makeText(_mActivity,"修改成功!",Toast.LENGTH_SHORT).show();
}
});
当该Fragment销毁时,在supportFragment的onDestroy方法中会处理返回结果数据,当然还是由代理类FragmentationDelegate处理
void handleResultRecord(Fragment from) {
SupportFragment preFragment = getPreFragment(from);
if(preFragment ==null)return;
Bundle args = from.getArguments();
if(args ==null|| !args.containsKey(FRAGMENTATION_ARG_RESULT_RECORD))return;
ResultRecord resultRecord = args.getParcelable(FRAGMENTATION_ARG_RESULT_RECORD);
if(resultRecord ==null)return;
preFragment.onFragmentResult(resultRecord.requestCode,resultRecord.resultCode,resultRecord.resultBundle);
}
通过当前的fragment找到前一个fragment也就是启动它的fragment,注意startForResult使用的是standard模式,然后调用preFragmentResult的onFragmentResult方法将序列化的bundle数据传回操作即可。
2.4Fragmentation懒加载
supportFragment的onLazyInitView方法主要用于数据的懒加载,view的初始化还是在createView时候完成。之所以采用懒加载是因为当类似微信这种多tab切换的fragment每个页面都有可能请求大量数据,而用户没有点击显示却要耗费资源去请求,用户体验会比较差。
onLazyInitView在当前view可视的时候开始加载。具体来看源码:
private void dispatchSupportVisible(boolean visible) {
.....//不重要代码
if(visible) {
mSupportFragment.onSupportVisible();
if(mIsFirstVisible) {
mIsFirstVisible=false;
mSupportFragment.onLazyInitView(mSaveInstanceState);
}
}
.....//不重要代码
}
上面的方法是VisibleDelegate中分发可视事件的方法,其中当第一次可视时会回调懒加载,接下来,
而VisibleDelegate会代理所有的生命周期方法,在相应的生命周期代理中会处理分发相应的可视性,最终调用dispatchSupportVisible方法。
随便举个例子:
public void onResume() {
if(!mIsFirstVisible) {
if(!mIsSupportVisible&& !mInvisibleWhenLeave&& isFragmentVisible(mSupportFragment)) {
mNeedDispatch=false;
dispatchSupportVisible(true);
}
}
}
VisibleDelegate的onResume方法里对dispatchSupportVisible方法的调用,而onResume又会在supportFragment的onResume方法中被调用。
2.5FragmentManager对回退栈管理。
FragmentManager在新的Fragment创建时会分配给一个Fragment下标,同时由mActive管理当前所有激活状态的Fragment。不过回退时mActive是对相应index置空而不是remove,需要注意的是
void makeInactive(Fragment f) {
if (f.mIndex < 0) { return; }
if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
mActive.set(f.mIndex, null);
if (mAvailIndices == null) {
mAvailIndices = new ArrayList();
}
mAvailIndices.add(f.mIndex);
mHost.inactivateFragment(f.mWho);
f.initState();
}
添加新的fragment源码
void makeActive(Fragment f) {
if (f.mIndex >= 0) { return; }
if (mAvailIndices == null || mAvailIndices.size() <= 0) {
if (mActive == null) { mActive = new ArrayList();
}
f.setIndex(mActive.size(), mParent); mActive.add(f);
} else {
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
}
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
需要注意的是mAvailIndices在pop多个Fragment时候,有可能会出现如下情况:
比如:正常堆栈顺序为A,B,C,D,现在Pop出C和D,堆栈变更为A,B,null,null,这个时候如果push新的Fragment,有可能会出现A,B,null,C的情况,原因是由于mAvailIndices在记录popFragment的时候,有可能先保存的较小的index后再保存较大index,也就是mAvailIndices里的Fragment的index顺序无法保证,这个时候需要做一次降序排列以解决问题