简析 addToBackStack使用和Fragment执行流程.docx
《简析 addToBackStack使用和Fragment执行流程.docx》由会员分享,可在线阅读,更多相关《简析 addToBackStack使用和Fragment执行流程.docx(11页珍藏版)》请在冰豆网上搜索。
简析addToBackStack使用和Fragment执行流程
简析addToBackStack使用和Fragment执行流程
简析addToBackStack使用和Fragment执行流程标签:
源码回退栈fragment2013-11-1414:
46
35385人阅读
评论(14)
收藏
举报分类:
android(6)版权声明:
本文为博主原创文章,未经博主允许不得转载。
在使用Fragment的时候我们一般会这样写:
FragmentTransactiontransaction=getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.content_view,fragment,fragment.getClass().getName());
//transaction.addToBackStack(null);
mitAllowingStateLoss();对于是否要加transaction.addToBackStack(null);也就是将Fragment加入到回退栈。
官方的说法是取决于你是否要在回退的时候显示上一个Fragment。
虽然知道这回事,但是在做项目的时候还是没有清楚的认识,只是习惯性的加上addToBackStack;查看源码后才了解到该神马时候加入回退栈。
首先看
voidaddBackStackState(BackStackRecordstate){
if(mBackStack==null){
mBackStack=newArrayList<BackStackRecord>();
}
mBackStack.add(state);
reportBackStackChanged();
}
可以看出,我们并不是将Fragment加入到回退栈,而是加了一个叫BackStackRecord的实例;那这个BackStackRecord到底是什么,简单的说一个BackStackRecord记录了一次操作。
finalclassBackStackRecordextendsFragmentTransactionimplementsFragmentManager.BackStackEntry,RunnablebackstackRecord继承了FragmentTransaction抽象类,获得了诸如add,remove,replace这些控制方法,所以我们控制Fragment时一直使用的getSupportFragmentManager().beginTransaction()其实就是返回一个BackStackRecord实例;
backstackRecord也维护了一个Op对象,Op对象的作用就是记录一次操作的动作和Fragment引用以及操作使用的动画;
staticfinalclassOp{
Opnext;
Opprev;
intcmd;
Fragmentfragment;
intenterAnim;
intexitAnim;
intpopEnterAnim;
intpopExitAnim;
ArrayList<Fragment>removed;
}最后backstackRecord也实现了Runnable接口,通过commit来启动自身,在run方法中又根据维护的Op对象进行不同的操作。
其实不同的Fragment操作就是在启动不同的BackStatcRecord线程。
下面我们已一次transaction.add操作为例:
此操作也就是调用BackStackRecord里的add方法,方法中维护一个Op来保存这次add操作和相应的Fragment;然后我们会调用commit方法来提交操作,实质上是启动实现了Runnable接口的BackStackRecord自身,在run方法中根据Op执行add分支的操作,这里面我们会调用FragmentManager的addFragment方法
publicvoidrun(){
......
switch(op.cmd){
caseOP_ADD:
{
Fragmentf=op.fragment;
f.mNextAnim=op.enterAnim;
mManager.addFragment(f,false);
}break;
......
mManager.moveToState(mManager.mCurState,mTransition,mTransitionStyle,true);
if(mAddToBackStack){
mManager.addBackStackState(this);
}
}
注意方法的最后会根据mAddToBackStack标识来判断是否加入到回退栈。
接下来在FragmentManager的addFragment方法中
publicvoidaddFragment(Fragmentfragment,booleanmoveToStateNow){
if(mAdded==null){
mAdded=newArrayList<Fragment>();
}
if(DEBUG)Log.v(TAG,"add:
"+fragment);
makeActive(fragment);//通过此方法将fragment加入到一个mActive列表里。
if(!
fragment.mDetached){
if(mAdded.contains(fragment)){
thrownewIllegalStateException("Fragmentalreadyadded:
"+fragment);
}
mAdded.add(fragment);
fragment.mAdded=true;
fragment.mRemoving=false;
if(fragment.mHasMenu&&fragment.mMenuVisible){
mNeedMenuInvalidate=true;
}
if(moveToStateNow){
moveToState(fragment);
}
}
}像上面注释里说的,通过makeActive方法将fragment加入到一个mActive列表。
这个列表在后面会用到。
但现在先来看看代码里用蓝色标记的两个方法,这是两个方法名相同的重载方法,他们最后都会调用一个非常重要的方法:
moveToState
voidmoveToState(Fragmentf,intnewState,inttransit,inttransitionStyle,booleankeepActive){
......
if(f.mState<newState){
......
switch(f.mState){
caseFragment.INITIALIZING:
......
caseFragment.CREATED:
if(newState>Fragment.CREATED){
......
}
caseFragment.ACTIVITY_CREATED:
caseFragment.STOPPED:
if(newState>Fragment.STOPPED){
f.performStart();
}
caseFragment.STARTED:
if(newState>Fragment.STARTED){
......
f.performResume();
......
}
}
}elseif(f.mState>newState){
switch(f.mState){
caseFragment.RESUMED:
if(newState<Fragment.RESUMED){
......
f.performPause();
......
}
caseFragment.STARTED:
if(newState<Fragment.STARTED){
f.performStop();
}
caseFragment.STOPPED:
if(newState<Fragment.STOPPED){
f.performReallyStop();
}
caseFragment.ACTIVITY_CREATED:
if(newState<Fragment.ACTIVITY_CREATED){
......
}
caseFragment.CREATED:
if(newState<Fragment.CREATED){
......
f.performDestroy();
......
}
}
}
}
f.mState=newState;
}
对于这个方法要说明三点:
第一:
方法里所有的分支只有
staticfinalintINITIALIZING=0;//Notyetcreated.
staticfinalintCREATED=1;//Created.
staticfinalintACTIVITY_CREATED=2;//Theactivityhasfinisheditscreation.
staticfinalintSTOPPED=3;//Fullycreated,notstarted.
staticfinalintSTARTED=4;//Createdandstarted,notresumed.
staticfinalintRESUMED=5;//Createdstartedandresumed.
这六种状态,好像不太够。
其实这很好理解,如果传来的新状态比fragment的当前状态大那就是处于创建过程,如果新状态比当前状态小那就是处于关闭过程。
闭上眼睛想一想就能转过弯儿了!
!
!
第二:
这里面所有的case分支都是没有break方法的,这样就能保证传来一个状态就能把这个状态之后的所有操作都执行一遍,例如创建时传INITIALIZING状态,就能执行INITIALIZING、CREATED、ACTIVITY_CREATED、STOPPED、STARTED这一流程的代码,而不需要我们挨个的每个状态都传;又例如我们重写回到fragment要调用start()方法,那只需要传STOPPED(创建时执行的是onStart)就可以,而不需要再传STARTED(创建时执行的是onResume)。
第三:
代码中的红色部分会调用FragmentActivity里的dispatchActivityXXX方法,这里面最终会调用另外一个重要方法,它也叫做moveToState(其实这个方法最终也是会去调用上面的moveToState方法):
voidmoveToState(intnewState,inttransit,inttransitStyle,booleanalways){
......
for(inti=0;i<mActive.size();i++){
Fragmentf=mActive.get(i);
if(f!
=null){
moveToState(f,newState,transit,transitStyle,false);
if(f.mLoaderManager!
=null){
loadersRunning|=f.mLoaderManager.hasRunningLoaders();
}
}
}if(!
loadersRunning){
startPendingDeferredFragments();
}
}
}这里面有个for循环,它会根据前面提到的mActive列表来调用存储fragment的moveToState方法(是上面的那个moveToState)。
所以如果我们使用show、hide而不是用add、remove来操作fragment显示与隐藏的话,就会发现一个问题,假设一个FragmentActivity已经创建了三个fragment并且隐藏,然后它在创建第四个fragment的时候,会发现已经隐藏的三个fragment也都运行了onresume方法。
这就是因为这三个fragment已经加入到mActive中,并且在创建第四个的时候循环调用了他们的resume方法。
现在回到最开始的问题,为什么说加入回退栈就可以实现按返回键退回到上一个fragment界面:
这就要看FragmentActivity里面的回退方法了
publicvoidonBackPressed(){
if(!
mFragments.popBackStackImmediate()){
finish();
}
}关键在判断条件,也就是popBackStackImmediate()方法的实现和他的返回值:
他的返回值是由popBackStackState(mActivity.mHandler,null,-1,0)提供的(注意参数是固定的)
booleanpopBackStackState(Handlerhandler,Stringname,intid,intflags){
if(mBackStack==null){
returnfalse;
}
if(name==null&&id<0&&(flags&POP_BACK_STACK_INCLUSIVE)==0){
intlast=mBackStack.size()-1;
if(last<0){
returnfalse;
}
finalBackStackRecordbss=mBackStack.remove(last);
bss.popFromBackStack(true);
reportBackStackChanged();
}else{
......
}
returntrue;
}注意方法的第一个判断条件:
如果mBackStack==null就直接returnfalse,这样就会直接执行FragmentActivity的finishi()方法,这也就是当我们不添加addToBackStack方法时按返回键不会返回上一个fragment界面而是直接退出程序的原因了。
若添加了addToBackStack方法,也就是mBackStack!
=null的情况下,根据固定的参数会进入蓝色代码段,在这里取出回退栈列表中的最后一条BackStackReco记录并执行它的popFromBackStack方法:
在这个方法里会根据BackStackRecord维护的Op对象来执行相应的操作,以replace操作为例:
caseOP_REPLACE:
{
Fragmentf=op.fragment;
if(f!
=null){
f.mNextAnim=op.popExitAnim;
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
}
if(op.removed!
=null){
for(inti=0;i<op.removed.size();i++){
Fragmentold=op.removed.get(i);
old.mNextAnim=op.popEnterAnim;
mManager.addFragment(old,false);
}
}
}break;从中可以清除的看出是把Op的当前fragment给remove掉,再把Op保存的oldfragment给add上,这样一来就会显示上一个界面了。
所以,根据不同的情景,当我们不需要fragment会退到上一个界面或者管理的fragment过多而不想保留BackStackRecord记录过度使用资源时,就可以加入回退栈。
上一篇java读书笔记-《java设计模式》-第4章-外观模式下一篇【转】HTTP304的理解
顶0
踩1
我的同类文章android(6)·【转】android获取所有安装的非系统应用·套用DatePickerDialog源码实现自定义样式的日期选择对话框·简析AndroidAdapter适配器的内部调用流程
·【转】打开eclipse点更新后,出现ThisAndroidSDKrequiresAndroidDeveloperToolkitversion22.0.0orabove.·【转】android中onMeasure初看,深入理解布局之一!
·APK程序的运行过程猜你在找
数据结构基础系列(3):
栈和队列企业最抢手的程序员-全栈工程师修成记Node.js(MEAN)全栈开发入门C语言系列之数据结构栈的运用基于J2EE实现Webservice项目实战ATM查看评论
11楼闻香识好2015-12-2511:
22发表[回复]我来总结一下这篇文章,当你需要点击返回键是返回上一个Fragment而不是直接退出Activity时,请加入返回栈概念。
ft.ackStackRecord(null),然后重写退出键的方法。
10楼y83504952015-12-0316:
23发表[回复]在4.0以后好像这个就改了name标识的是:
返回栈的标识符。
9楼androidxiong2015-12-0313:
33发表[回复]刚刚在看源码,搜一下发现有高手写了这些分析,真精彩8楼j_l_k2015-11-2017:
59发表[回复]能给个demo吗469121903@7楼androidfordahai2014-09-1118:
32发表[回复]就是吐槽一下头像....Re:
Mikite2015-06-1810:
19发表[回复]回复androidfordahai:
恩6楼Martinmu20132014-08-1809:
56发表[回复]楼主你的安卓框架源码的地址能发我一份吗?
290709630@,谢谢。
楼主好人一身平安。
5楼若鱼19192014-08-0813:
48发表[回复]能否实现对栈里面的非栈顶fragment的删除操作?
4楼QianrushiRuanjianzu2014-07-1618:
40发表[回复]受教了写的很棒3楼CYoung2014-07-0300:
09发表[回复]写的有深度2楼xiandan2014-03-0417:
22发表[回复]引用“米老唐鸭”的评论:
根据不同的情景,当我们不需要fragment会退到上一个界面或者管理的fragment过多而不想保留BackStackRecord记录过度使用资源时,就可以加入回退栈
是不是说反了Re:
yanghui19865272014-05-2116:
12发表[回复]回复xiandan:
我也感觉说反了。
Re:
geduo_832014-08-0411:
07发表[回复]回复yanghui1986527:
确实是说反了
///////////////////////////////////
如果你想要记录之前的Fragment,就添加addToBackStack(),这仅仅是一个帮助类而已(姑且是这么理解),如果不需要记录的话,可以不用这个方法。