Fragment回退栈管理

来源:互联网 发布:充值系统源码 编辑:程序博客网 时间:2024/06/05 06:15

一般操作一个fragment是使用fragmentTransaction,fragmentTransaction由fragmentManager获取。

manager = getSupportFragmentManager();FragmentTransaction        transaction = manager.beginTransaction();

实际上manager是个FragmentManagerImpl对象,而beginTransaction返回的是一个BackStackRecord具体对象

@Override    public FragmentTransaction beginTransaction() {        return new BackStackRecord(this);    }

FragmentTransaction有多个方法,add,remove,replace(实际上是remove和add的结合),hide,show,detach,attach,addToBackStack。。。

最后都会调用commit来完成一系列动作的提交。
transaction.add()会执行生命周期

06-29 18:43:47.332 9099-9099/com.txt.mydemo I/OneFragment: onAttach06-29 18:43:47.332 9099-9099/com.txt.mydemo I/OneFragment: onCreate06-29 18:43:47.332 9099-9099/com.txt.mydemo I/OneFragment: onCreateView06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onViewCreated06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onActivityCreated06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onStart06-29 18:43:47.340 9099-9099/com.txt.mydemo I/OneFragment: onResume

transaction.remove会执行生命周期

06-29 18:45:30.699 9099-9099/com.txt.mydemo I/OneFragment: onPause06-29 18:45:30.699 9099-9099/com.txt.mydemo I/OneFragment: onStop06-29 18:45:30.699 9099-9099/com.txt.mydemo I/OneFragment: onDestroyView06-29 18:45:30.942 9099-9099/com.txt.mydemo I/OneFragment: onDestroy06-29 18:45:30.942 9099-9099/com.txt.mydemo I/OneFragment: onDetach

而使用hide、show则不会执行生命周期,其实就类似于view的setvisible设置可见性

如下这个示例:

public class FragmentActivity extends AppCompatActivity implements OneFragment.OnFragmentInteractionListener{    private FragmentManager manager;    private static final String TAG1 = "TAG1", TAG2 = "TAG2";//    private FragmentTransaction transaction;//只能作为局部变量,一次只能执行一个fragment的commit;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_fragment_test);        manager = getSupportFragmentManager();    }    public void tab1(View view){        Fragment fragment = manager.findFragmentByTag(TAG1);        FragmentTransaction        transaction = manager.beginTransaction();        //设置动画效果,必须在add ,replace,remove之前调用        //使用系统默认的动画        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);        //使用自定义的动画        transaction.setCustomAnimations(R.anim.slide_in_from_right,R.anim.slide_in_from_right);        if(manager.findFragmentByTag(TAG2) != null){            transaction.hide(manager.findFragmentByTag(TAG2));        }        if( fragment != null ){            transaction.show(fragment);        }else {            transaction.add(R.id.content, OneFragment.newInstance("1", "2"), TAG1);//            transaction.addToBackStack(null);//添加到回退栈中,在这里只是记录一个标记,其实是在commit中,调用fragmentManager的allocBackStackIndex方法,所以回退栈是由fragmentManager管理的。必须在commit之前        }        transaction.commit();    }    public void tab2(View view){        Fragment fragment = manager.findFragmentByTag(TAG2);        FragmentTransaction transaction = manager.beginTransaction();        if (manager.findFragmentByTag(TAG1) != null) {            transaction.hide(manager.findFragmentByTag(TAG1));        }        if( fragment != null ) {            transaction.show(fragment);        }else {            transaction.add(R.id.content, TwoFragment.newInstance("abc"), TAG2);//            transaction.addToBackStack(null);        }        transaction.commit();    }    //activity向fragment传值    private void setTextToFragment(){        ((OneFragment)manager.findFragmentByTag("tab1")).setTextByActivity();    }    //实现tab1的回调接口(即fragment向activity传值)    @Override    public void onFragmentInteraction(Uri uri) {    }}

使用hide和show来切换连个fragment,并且添加fragment的时候不加入回退栈,那么在activity中按back键的时候,两个fragment的生命周期是这样的:

06-29 18:55:34.996 25403-25403/com.txt.mydemo I/OneFragment: onPause06-29 18:55:34.996 25403-25403/com.txt.mydemo I/TwoFragment: onPause06-29 18:55:35.106 25403-25403/com.txt.mydemo W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (2560x1440, max=2048x2048)06-29 18:55:35.496 25403-25403/com.txt.mydemo I/OneFragment: onStop06-29 18:55:35.496 25403-25403/com.txt.mydemo I/TwoFragment: onStop06-29 18:55:35.496 25403-25403/com.txt.mydemo I/OneFragment: onDestroyView06-29 18:55:35.504 25403-25403/com.txt.mydemo I/OneFragment: onDestroy06-29 18:55:35.504 25403-25403/com.txt.mydemo I/OneFragment: onDetach06-29 18:55:35.504 25403-25403/com.txt.mydemo I/TwoFragment: onDestroyView06-29 18:55:35.504 25403-25403/com.txt.mydemo I/TwoFragment: onDestroy06-29 18:55:35.504 25403-25403/com.txt.mydemo I/TwoFragment: onDetach
一起结束。

把addToBackStack的注释去掉,则根据添加的先后顺序,依次销毁fragment,不管中间切换过几次hide或show
比如先添加tab2,在添加tab1,back键销毁的是这样的:

06-29 19:07:22.567 30394-30394/com.txt.mydemo I/OneFragment: onPause06-29 19:07:22.567 30394-30394/com.txt.mydemo I/OneFragment: onStop06-29 19:07:22.567 30394-30394/com.txt.mydemo I/OneFragment: onDestroyView06-29 19:07:22.824 30394-30394/com.txt.mydemo I/OneFragment: onDestroy06-29 19:07:22.824 30394-30394/com.txt.mydemo I/OneFragment: onDetach                                                                                                                          --------- beginning of /dev/log/system06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onPause06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onStop06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onDestroyView06-29 19:07:24.824 30394-30394/com.txt.mydemo I/TwoFragment: onDestroy06-29 19:07:24.832 30394-30394/com.txt.mydemo I/TwoFragment: onDetach

先销毁了后加入的oneFragment。

接着看下replace时的生命周期,先点击tab1按钮,添加oneFragment到界面上,然后点击replaceTab1按钮,添加twoFragment到界面上。在操作这两步的时候,把addTOBackStack方法注释掉,看下效果:

增加了replaceTab1方法

public class FragmentActivity extends AppCompatActivity implements OneFragment.OnFragmentInteractionListener{    private FragmentManager manager;    private static final String TAG1 = "TAG1", TAG2 = "TAG2";//    private FragmentTransaction transaction;//只能作为局部变量,一次只能执行一个fragment的commit;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_fragment_test);        manager = getSupportFragmentManager();    }    public void tab1(View view){//        Fragment fragment = manager.findFragmentByTag(TAG1);        FragmentTransaction        transaction = manager.beginTransaction();        //设置动画效果,必须在add ,replace,remove之前调用        //使用系统默认的动画        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);        //使用自定义的动画        transaction.setCustomAnimations(R.anim.slide_in_from_right,R.anim.slide_in_from_right);//        if(manager.findFragmentByTag(TAG2) != null){//            transaction.hide(manager.findFragmentByTag(TAG2));//        }//        if( fragment != null ){//            transaction.show(fragment);//        }else {            transaction.add(R.id.content, OneFragment.newInstance("1", "2"), TAG1);//            transaction.addToBackStack(null);//添加到回退栈中,在这里只是记录一个标记,其实是在commit中,调用fragmentManager的allocBackStackIndex方法,所以回退栈是由fragmentManager管理的。必须在commit之前//        }        transaction.commit();    }    public void tab2(View view){        Fragment fragment = manager.findFragmentByTag(TAG2);        FragmentTransaction transaction = manager.beginTransaction();        if (manager.findFragmentByTag(TAG1) != null) {            transaction.hide(manager.findFragmentByTag(TAG1));        }        if( fragment != null ) {            transaction.show(fragment);        }else {            transaction.add(R.id.content, TwoFragment.newInstance("abc"), TAG2);            transaction.addToBackStack(null);        }        transaction.commit();    }    public void replaceTab1(View view){        FragmentTransaction transaction = manager.beginTransaction();        transaction.replace(R.id.content, TwoFragment.newInstance("abc"), TAG2);//        transaction.addToBackStack(null);        transaction.commit();    }    //activity向fragment传值    private void setTextToFragment(){        ((OneFragment)manager.findFragmentByTag("tab1")).setTextByActivity();    }    //实现tab1的回调接口(即fragment向activity传值)    @Override    public void onFragmentInteraction(Uri uri) {    }}


1、把addToBackStack都注释掉

添加onefragment

06-29 19:38:59.629 30449-30449/com.txt.mydemo I/OneFragment: onAttach06-29 19:38:59.629 30449-30449/com.txt.mydemo I/OneFragment: onCreate06-29 19:38:59.629 30449-30449/com.txt.mydemo I/OneFragment: onCreateView06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onViewCreated06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onActivityCreated06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onStart06-29 19:38:59.645 30449-30449/com.txt.mydemo I/OneFragment: onResume
使用replace把twofragment添加上去,很明显,没有把任何fragment加入回退栈,那么很明显,onefragment此时要被销毁了。
06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onPause06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onStop06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onDestroyView06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onDestroy06-29 19:39:48.488 30449-30449/com.txt.mydemo I/OneFragment: onDetach06-29 19:39:48.488 30449-30449/com.txt.mydemo I/TwoFragment: onAttach06-29 19:39:48.488 30449-30449/com.txt.mydemo I/TwoFragment: onCreate06-29 19:39:48.488 30449-30449/com.txt.mydemo I/TwoFragment: onCreateView06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onViewCreated06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onActivityCreated06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onStart06-29 19:39:48.504 30449-30449/com.txt.mydemo I/TwoFragment: onResume
按返回键,直接退出activity,twofragment也销毁了
06-29 19:40:14.559 30449-30449/com.txt.mydemo I/TwoFragment: onPause06-29 19:40:14.676 30449-30449/com.txt.mydemo W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (2560x1440, max=2048x2048)06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onStop06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onDestroyView06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onDestroy06-29 19:40:15.035 30449-30449/com.txt.mydemo I/TwoFragment: onDetach


2、把tab1方法里面的addToBackStack方法注释去掉,看下日志:
添加tab1
06-29 19:49:31.160 5569-5569/com.txt.mydemo I/OneFragment: onAttach06-29 19:49:31.160 5569-5569/com.txt.mydemo I/OneFragment: onCreate06-29 19:49:31.160 5569-5569/com.txt.mydemo I/OneFragment: onCreateView06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onViewCreated06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onActivityCreated06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onStart06-29 19:49:31.176 5569-5569/com.txt.mydemo I/OneFragment: onResume
replaceTab1,oneFragment只是执行了销毁view的操作。

06-29 19:49:57.863 5569-5569/com.txt.mydemo I/OneFragment: onPause06-29 19:49:57.863 5569-5569/com.txt.mydemo I/OneFragment: onStop06-29 19:49:57.863 5569-5569/com.txt.mydemo I/OneFragment: onDestroyView06-29 19:49:57.863 5569-5569/com.txt.mydemo I/TwoFragment: onAttach06-29 19:49:57.863 5569-5569/com.txt.mydemo I/TwoFragment: onCreate06-29 19:49:57.863 5569-5569/com.txt.mydemo I/TwoFragment: onCreateView06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onViewCreated06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onActivityCreated06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onStart06-29 19:49:57.879 5569-5569/com.txt.mydemo I/TwoFragment: onResume
按back键,异常出来了,onefragment执行了销毁的操作(从activity脱离),而界面没有发生变化,还是显示twofragment的界面。
06-29 19:50:35.942 5569-5569/com.txt.mydemo I/OneFragment: onDestroy06-29 19:50:35.942 5569-5569/com.txt.mydemo I/OneFragment: onDetach
再按back键后,twoFragment和activity一起销毁了

这是为什么呢?
我的理解是,oneFragment加入了回退栈,而twoFragment没有加入,那么oneFragment就能处理back键。所以按back键的时候,oneFragment执行了销毁,而twoFragment依旧附着在activity上,再按back键,和activity一起销毁。


3、那么再来看另一种情况,把tab1里面的addToBackStack注释掉,打开replaceTab1的addToBackStack
添加tab1
06-29 20:05:45.520 18607-18607/com.txt.mydemo I/OneFragment: onAttach06-29 20:05:45.520 18607-18607/com.txt.mydemo I/OneFragment: onCreate06-29 20:05:45.527 18607-18607/com.txt.mydemo I/OneFragment: onCreateView06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onViewCreated06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onActivityCreated06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onStart06-29 20:05:45.535 18607-18607/com.txt.mydemo I/OneFragment: onResume
replaceTab1,把tab2加进来,替换了tab1,但是tab1并没有执行销毁的操作(即从activity脱离)
06-29 20:06:15.512 18607-18607/com.txt.mydemo I/OneFragment: onPause06-29 20:06:15.512 18607-18607/com.txt.mydemo I/OneFragment: onStop06-29 20:06:15.512 18607-18607/com.txt.mydemo I/OneFragment: onDestroyView06-29 20:06:15.512 18607-18607/com.txt.mydemo I/TwoFragment: onAttach06-29 20:06:15.512 18607-18607/com.txt.mydemo I/TwoFragment: onCreate06-29 20:06:15.512 18607-18607/com.txt.mydemo I/TwoFragment: onCreateView06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onViewCreated06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onActivityCreated06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onStart06-29 20:06:15.527 18607-18607/com.txt.mydemo I/TwoFragment: onResume
按back键,twoFragment响应了回退操作,从activity脱离,然后重新创建了oneFragment的view,不是说只有加入了回退栈才能保存状态吗,明明oneFragment加入的时候没有设置addToBackStack啊?replace操作不是等于remove+add吗,难道oneFragment没有被移除?难道addToBackStack的意思是,让当前fragment能响应back事件,而在其之间的fragment能保存状态,难道理解一直是错误的?
06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onPause06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onStop06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onDestroyView06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onDestroy06-29 20:07:34.090 18607-18607/com.txt.mydemo I/TwoFragment: onDetach06-29 20:07:34.090 18607-18607/com.txt.mydemo I/OneFragment: onCreateView06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onViewCreated06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onActivityCreated06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onStart06-29 20:07:34.098 18607-18607/com.txt.mydemo I/OneFragment: onResume
再按back键,oneFragment和activity一起销毁
06-29 20:08:57.488 18607-18607/com.txt.mydemo I/OneFragment: onPause06-29 20:08:57.598 18607-18607/com.txt.mydemo W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (2560x1440, max=2048x2048)06-29 20:08:58.027 18607-18607/com.txt.mydemo I/OneFragment: onStop06-29 20:08:58.027 18607-18607/com.txt.mydemo I/OneFragment: onDestroyView06-29 20:08:58.035 18607-18607/com.txt.mydemo I/OneFragment: onDestroy06-29 20:08:58.035 18607-18607/com.txt.mydemo I/OneFragment: onDetach


4、再来看下,把tab1方法中的addToBackStack加上,也就是把oneFragment也加入回退栈中
点击tab1
06-29 20:17:58.027 30768-30768/com.txt.mydemo I/OneFragment: onAttach06-29 20:17:58.027 30768-30768/com.txt.mydemo I/OneFragment: onCreate06-29 20:17:58.027 30768-30768/com.txt.mydemo I/OneFragment: onCreateView06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onViewCreated06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onActivityCreated06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onStart06-29 20:17:58.035 30768-30768/com.txt.mydemo I/OneFragment: onResume
点击replaceTab1
06-29 20:18:30.387 30768-30768/com.txt.mydemo I/OneFragment: onPause06-29 20:18:30.387 30768-30768/com.txt.mydemo I/OneFragment: onStop06-29 20:18:30.387 30768-30768/com.txt.mydemo I/OneFragment: onDestroyView06-29 20:18:30.387 30768-30768/com.txt.mydemo I/TwoFragment: onAttach06-29 20:18:30.387 30768-30768/com.txt.mydemo I/TwoFragment: onCreate06-29 20:18:30.387 30768-30768/com.txt.mydemo I/TwoFragment: onCreateView06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onViewCreated06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onActivityCreated06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onStart06-29 20:18:30.395 30768-30768/com.txt.mydemo I/TwoFragment: onResume
按back键,应该是twoFragment销毁了,果然,twoFragment执行了销毁操作,oneFragment重新回到了前台,这和3的情况是一样的
06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onPause06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onStop06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onDestroyView06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onDestroy06-29 20:20:07.746 30768-30768/com.txt.mydemo I/TwoFragment: onDetach06-29 20:20:07.746 30768-30768/com.txt.mydemo I/OneFragment: onCreateView06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onViewCreated06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onActivityCreated06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onStart06-29 20:20:07.762 30768-30768/com.txt.mydemo I/OneFragment: onResume
再按back键,oneFragment从activity中移除,相应了back键,但是activity却没有销毁。
06-29 20:26:34.926 30768-30768/com.txt.mydemo I/OneFragment: onPause06-29 20:26:34.926 30768-30768/com.txt.mydemo I/OneFragment: onStop06-29 20:26:34.926 30768-30768/com.txt.mydemo I/OneFragment: onDestroyView06-29 20:26:35.176 30768-30768/com.txt.mydemo I/OneFragment: onDestroy06-29 20:26:35.176 30768-30768/com.txt.mydemo I/OneFragment: onDetach


疑问?
之前一直理解,addToBackStack是把当前fragment加入到回退栈中,被replace的时候能保存状态值。
现在看来,addToBackStack只是能让当前的fragment响应back键,而可以让replace之前的fragment保存状态。


addToBackStack的源码分析

另外从源码中可以看出,addToBackStack方法只是为当前的fragmentTransaction设置了一个标记,真正执行入栈的还是在调用了commit后,由fragmentManager来执行,

BackStackRecord中的设置标记mAddToBackStack为true:

public FragmentTransaction addToBackStack(String name) {        if (!mAllowAddToBackStack) {            throw new IllegalStateException(                    "This FragmentTransaction is not allowed to be added to the back stack.");        }        mAddToBackStack = true;        mName = name;        return this;    }

commit方法:

@Override    public int commit() {        return commitInternal(false);    }
int commitInternal(boolean allowStateLoss) {        if (mCommitted) throw new IllegalStateException("commit already called");        if (FragmentManagerImpl.DEBUG) {            Log.v(TAG, "Commit: " + this);            LogWriter logw = new LogWriter(TAG);            PrintWriter pw = new PrintWriter(logw);            dump("  ", null, pw, null);            pw.close();        }        mCommitted = true;        if (mAddToBackStack) {            mIndex = mManager.allocBackStackIndex(this);        } else {            mIndex = -1;        }        mManager.enqueueAction(this, allowStateLoss);        return mIndex;    }
调用了manager的allocBackStackIndex方法来入栈。
具体细节参考:简析 addToBackStack使用和Fragment执行流程