Fragment详解
Fragment是Android3.0开始引入的组件,主要是为了便于大屏UI的设计和实现。它有自己的生命周期,但是它的生命周期会受到加载Fragment的Activity的生命周期的约束。在开始讲解前,老规矩,首先提出问题:
一、Fragment的生命周期分析
Fragment的生命周期主要有onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->onPause->onStop()->onDestoryView()->onDestory()->onDetach()。
具体生命周期如下图 1.
图 1
(1)onAttach()
将Fragment和Activity相关联。在使用时我们通常会使用以下代码添加fragment
getSupportFragmentManager().beginTransaction()
.add(R.id.
frame_container
,
msg_fragment
);
此时将fragment添加到Activity中,则会首先调用onAttach()方法。
(2)onCreate()
创建Fragment对象
(3)onCreateView()
在创建Fragment视图时调用
1. public View onCreateView(LayoutInflater inflater, ViewGroup container,
2. Bundle savedInstanceState) {
3. return inflater.inflate(R.layout.fragment3, container, false);
4. }
通过LayoutInflater的inflate()方法,来绑定xml文件,从而完成视图创建
这里的inflate()方法,
第一个参数为布局文件,
第二个参数为ViewGroup root,表示父窗口,如果返回null,表示没有父窗口,则该Fragment无法显示;
第三个参数,attchToRoot,如果为true,则表示,当前Fragment的xml的根节点作为整个Activity的根节点,此时Acrtivity的其他控件都将不显示。
(4)onActivityCreated()
在Activity的onCreate()方法结束后调用。此时Activity已经完成创建。但此时Activity并不一定是在onCreate()生命周期,可能在onResume()周期中,原因前面也讲过,因为Fragment的添加在Activity的onResume()时进行,也可能是在Activity的onCreate()时进行。
(5)onStart()
与Activity的onStart()相绑定,此时Fragment对于用户已经可见,但是无法交互。
(6)onResume()
与Activity的onResume()相绑定,在onResume()方法之后就可以与用户交互了。
(7)onPause()
与 Activity的onPause()想绑定,且意义与Activity相同
(8)onStop()
与Activity的onStop()相绑定,意义也与Activity相同。
(9)onDestoryView()
在Fragment即将结束结束或者保存,此时会回调此方法,用于将onCreateView的视图和fragment分离,而要重新显示的话,需要重新创建视图,介于Activity的onStop和onDestory()之间调用。
例如我们在使用remove某一个Fragment时,但是这个Fragment加入了回退栈,此时则会调用
onDestoryView()销毁视图,但是不会调用onDestory()。(具体后面会有相应的例子,别急。)
生命周期如下:
2018-05-16 18:06:09.452 30265-30265/com.sky_wf.chinachat D/ChinaChat: , <> ||| com.sky_wf.chinachat.activity.fragment.Fragment_Msg$onStop()@52
2018-05-16 18:06:09.459 30265-30265/com.sky_wf.chinachat D/ChinaChat: , <> ||| com.sky_wf.chinachat.activity.fragment.Fragment_Msg$onDestroyView()@58
重新add这个Fragent,则生命周期如下:
2018-05-16 18:06:12.936 30265-30265/com.sky_wf.chinachat D/ChinaChat: , <> ||| com.sky_wf.chinachat.activity.fragment.Fragment_Msg$onCreateView()@38
2018-05-16 18:06:12.947 30265-30265/com.sky_wf.chinachat D/ChinaChat: , <> ||| com.sky_wf.chinachat.activity.fragment.Fragment_Msg$onActivityCreated()@46
会发现,由于Fragment对象并没有销毁且仍然和Activity绑定,因此此时只调用onCreateView->onActivityCreated()
(10)onDestory()
和Activity的onDestory()绑定,在这个过程中Fragment对象销毁,但仍然和Activity绑定
(11)onDetach()
二、FragmentTransaction和FragmentManager的分析
1、FragmentManager
FragmentManger是Fragment的管理器,主要用来对Activity中的Fragment进行管理,比如获取Fragmen事务、执行回退栈的出栈等。
它是一个抽象类,因此通常我们使用它的子类FragmentManagerImpl。
接下来我们来看FragmentManagerImpl的常用方法,如下表 1:
方法名
|
方法说明
|
public
FragmentTransaction beginTransaction()
|
获取FragmentTransaction对象
|
public
Fragment findFragmentById(
int
id)
|
根据id获取对应的Fragment
|
public
Fragment findFragmentByTag(String tag)
|
根据tag获取对应的Fragment
|
boolean
popBackStackState
|
执行回退栈的出栈
|
public int
getBackStackEntryCount()
|
获取回退栈的大小
|
表 1
2、FragmentTransaction
FragmentTransaction是Fragment事务类, 主要用于对Activity中Fragment进行操作,比如add,remove,replace,hide,,show等。
它是一个抽象类,通常我们使用它的子类
BackStackRecord。
方法名
|
方法说明
|
FragmentTransaction add(Fragment var1
,
String var2)
;
FragmentTransaction add(@IdRes
int
var1
,
Fragment var2)
;
FragmentTransaction add(@IdRes
int
var1
,
Fragment var2
,
@Nullable String var3)
;
|
将Fragment添加到Activity的Container中
|
public
FragmentTransaction replace(
int
containerViewId
,
Fragment fragment)
|
移除Container中的Fragment,替换为该Fragment
|
public
FragmentTransaction remove(Fragment fragment)
|
从Container中移除该Fragment
|
public
FragmentTransaction hide(Fragment fragment)
|
隐藏参数中给出的Fragment
|
public
FragmentTransaction show(Fragment fragment)
|
显示参数中给出Fragment
|
public int
commit()
|
执行完上述操作后,必须调用commit进行提交
|
public
FragmentTransaction addToBackStack(String name)
|
将当前事务(在commit之前)中所有的Fragment加入到回退栈中
|
三、实例讲解
接下来我们将通过Demo来讲解Fragmen的具体用法,并具体讲解了hide、show、replace、add、remove的方法的使用。
源码地址: https://github.com/DaMiaoKobe/ChinaChat
这里我们定义一个四个Fragment和一个MainActivity
-
Fragment_Msg
-
Fragment_Friends
-
Fragment_Discover
-
Fragment_Porfile
-
MainActivity
1、Fragment_Msg
其他三个Fragment只是类名不一样方法都相同,这里就不一一列举了
/**
* @Date : 2018/5/14
* @Author : WF
* @Description :Msg界面
*/
public class Fragment_Msg extends BaseFragment {
private final String TAG = "Fragment_Msg";
@Override
public void onAttach(Context context) {
super.onAttach(context);
Debugger.d(TAG,"<>");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Debugger.d(TAG,"<>");
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Debugger.d(TAG,"<>");
View view = inflater.inflate(R.layout.fragment_msg,container,false);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Debugger.d(TAG,"<>");
}
@Override
public void onStop() {
super.onStop();
Debugger.d(TAG,"<>");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Debugger.d(TAG,"<>");
}
@Override
public void onDestroy() {
super.onDestroy();
Debugger.d(TAG,"<>");
}
}
2、MainActivity
public class MainActivity extends BaseFragmentActivity
{
private Fragment[] fragments;
private Fragment_Msg msg_fragment;
private Fragment_Friends friend_fragment;
private Fragment_Discover discover_fragment;
private Fragment_Porfile profile_fragment;
private TextView unreadMsgLable;
private TextView unreadAdressLable;
private TextView unreadFindLable;
private TextView unreadProfileLable;
private ImageView[] imageBottom;// 底部img
private TextView[] txtBottom;//底部txt
private TextView txt_title;
private ImageView img_right;
private int currentTabIndex = 0;// 当前Fragment的index
private int index = 0;
private final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Debugger.d(TAG, ">>onCreate<<");
findViewById();
initViews();
initTabView();
}
private void initViews() {
img_right.setVisibility(View.VISIBLE);
img_right.setImageResource(R.drawable.icon_add);
txt_title.setVisibility(View.VISIBLE);
}
private void findViewById()
{
img_right = (ImageView)findViewById(R.id.img_right);
txt_title = (TextView)findViewById(R.id.txt_left);
}
private void initTabView() {
unreadMsgLable = (TextView)findViewById(R.id.unread_msg_number);
unreadAdressLable = (TextView)findViewById(R.id.unread_friend_number);
unreadFindLable = (TextView)findViewById(R.id.unread_discover_number);
unreadProfileLable = (TextView)findViewById(R.id.unread_profile_number);
msg_fragment = new Fragment_Msg();
friend_fragment = new Fragment_Friends();
discover_fragment = new Fragment_Discover();
profile_fragment = new Fragment_Porfile();
fragments = new Fragment[]{msg_fragment,friend_fragment,discover_fragment,profile_fragment};
imageBottom = new ImageView[4];
imageBottom[0] = (ImageView)findViewById(R.id.img_chinachat);
imageBottom[1] = (ImageView)findViewById(R.id.img_friend);
imageBottom[2] = (ImageView)findViewById(R.id.img_discover);
imageBottom[3] = (ImageView)findViewById(R.id.img_profile);
imageBottom[0].setSelected(true);
txtBottom = new TextView[4];
txtBottom[0] = (TextView)findViewById(R.id.txt_chinachat);
txtBottom[1] = (TextView)findViewById(R.id.txt_friend);
txtBottom[2] = (TextView)findViewById(R.id.txt_discover);
txtBottom[3] = (TextView)findViewById(R.id.txt_profile);
txtBottom[0].setTextColor(0xFF45C01A);
//添加四个Fragment,并默认显示msg_fragment
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_container,msg_fragment)
.add(R.id.frame_container,friend_fragment)
.add(R.id.frame_container,discover_fragment)
.add(R.id.frame_container,profile_fragment)
.hide(friend_fragment)
.hide(profile_fragment)
.hide(discover_fragment)
.commit();
}
/**根据点击,呈现对应的Fragment
* @param view
*/
public void onTabClicked(View view)
{
switch (view.getId())
{
case R.id.bottom_chinachat:
index = 0;
txt_title.setText("喵信");
break;
case R.id.bottom_friend:
index = 1;
break;
case R.id.bottom_discover:
index = 2;
break;
case R.id.bottom_profile:
index = 3;
break;
}
if(currentTabIndex!=index)
{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.hide(fragments[currentTabIndex]);
if(!fragments[index].isAdded())
{
transaction.add(R.id.frame_container,fragments[index]);
}
transaction.show(fragments[index]);
transaction.commit();
}
imageBottom[currentTabIndex].setSelected(false);
imageBottom[index].setSelected(true);
txtBottom[currentTabIndex].setTextColor(0xFF999999);
txtBottom[index].setTextColor(0xFF45C01A);
currentTabIndex = index;
}
@Override
protected void onStop() {
super.onStop();
Debugger.d(TAG, ">>onStop<<");
}
@Override
protected void onDestroy() {
super.onDestroy();
Debugger.d(TAG, ">>onDestroy<<");
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (event.getKeyCode())
{
case KeyEvent.KEYCODE_BACK:
// FragmentManager manager = getSupportFragmentManager();
// manager.popBackStack();
// return true;
}
return super.onKeyDown(keyCode, event);
}
}
3、效果图
在第一次进入时,会显示第一个tab内容,也就是一个TextView显示喵信,而点击底部其他tab,则对应会切换到它们的Fragment。
4、分析与讲解:
这里是一个放微信的主界面,在点击下面四个tab,上面对应的TextView会显示响应的title。接下来我们来看代码。
(1)第一次进入Activity时显示某一个Fragment
这里我们在Activity的onCreate()方法中做了如下处理:
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_container,msg_fragment)
.add(R.id.frame_container,friend_fragment)
.add(R.id.frame_container,discover_fragment)
.add(R.id.frame_container,profile_fragment)
.hide(friend_fragment)
.hide(profile_fragment)
.hide(discover_fragment)
.commit();
这里我们
getSupportFragmentManager().beginTransaction()获取到FragmentTransaction对象,然后调用add()方法将四个Fragment分别添加到Activity的Container中,由于我们初始化进入时需要显示在第一个Tab喵信上,因此这里我们调用hide()方法隐藏其他三个Fragment,当然最后需要我们调用commit()方法提交事务。
运行程序,但不点击底部tab,则只是执行上面的add和hide操作。log如下图 1:
分析log,可以发现四个Fragment均执行了onAttach()-》onCreate()-》onCreateView()-》onActivityCreated()-》onStart()-》onResume()
并且在对三个Fragment执行hide操作后,仍然处于onResume(),因此可以说明以下两点:
(1)add操作:
将Fragment添加到Activity的Container,并且添加的Fragment的生命周期和Activity当前生命周期一致,均处于onResume(),无论是否是当前正在显示的Fragment。
需要注意:
已经添加过的Fragment对象,如果重复添加,则会报错
java.lang.IllegalStateException: Fragment already added: Fragment_Friends{e0793ae #1 id=0x7f0b0087}
(2)hide操作:
在使用hide()隐藏其他三个Fragment界面的内容后,这三个Fragment的生命周期并没有改变,说明它的效果类似于我们常用的setVisiable(View.InVisiable),也就是我们常说的可见不可见。
这里如果我们不使用hide,只是add(),效果如下 图:
我们会发现,此时四个Fragment的内容叠加在一起。
(3)show操作
show操作和 我们的hide()相对应,它代表设置参数中的Fragment可见。
public void onCreate()
{
........
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_container,msg_fragment)
.add(R.id.frame_container,friend_fragment)
.add(R.id.frame_container,discover_fragment)
.add(R.id.frame_container,profile_fragment)
.hide(friend_fragment)
.hide(profile_fragment)
.hide(discover_fragment)
.show(msg_fragment)
.show(friend_fragment)//增加show(friend_fragment)
.commit()
;
}
效果图如下:
可以看到,我们对
friend_fragment
先hide(),然后再调用show()方法,此时
friend_fragment
可见。
2、点击底部tab,切换Fragment
这里我们看点击事件的处理方法onTabClicked()
public void onTabClicked(View view)
{
switch (view.getId())
{
case R.id.bottom_chinachat:
index = 0;
txt_title.setText("喵信");
break;
case R.id.bottom_friend:
index = 1;
break;
case R.id.bottom_discover:
index = 2;
break;
case R.id.bottom_profile:
index = 3;
break;
}
if(currentTabIndex!=index)
{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.hide(fragments[currentTabIndex]);
if(!fragments[index].isAdded())
{
transaction.add(R.id.frame_container,fragments[index]);
}
transaction.show(fragments[index]);
transaction.commit();
}
imageBottom[currentTabIndex].setSelected(false);
imageBottom[index].setSelected(true);
txtBottom[currentTabIndex].setTextColor(0xFF999999);
txtBottom[index].setTextColor(0xFF45C01A);
currentTabIndex = index;
}
这里我们在点击不同的tab时,呈现对应的Fragment。同样我们是通过前面提到的hide()和show()来完成对应的隐藏和呈现操作。
那么我们只能通过show()和hide()来实现Fragment的显示和隐藏么?显然不是,接下来我们再来看其他实现方法:
(1)remove操作
我们对上面的代码做一些修改,不使用hide()来隐藏当前正在显示的Fragment,而使用remove来执行。具体修改如下:
if(currentTabIndex!=index)
{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.remove(fragments[currentTabIndex]);//替换之前的transaction.hide(fragments[currentTabIndex]);
if(!fragments[index].isAdded())
{
transaction.add(R.id.frame_container,fragments[index]);
}
transaction.show(fragments[index]);
transaction.commit();
}
我们在隐藏其他Fragment时使用replace()代替hide(),运行程序,点击tab,则log如下图:
可以看到我们使用remove()来隐藏Fragment,则对应的Fragment会依次调用onStop()-》onDestoryView()-》onDestory()-》onDetach()(这里我的程序里没有写onDetach(),但实际上会调用的),也就是说在调用remove后,对应的Fragment会销毁并与Activity解绑。
而remove的Fragment要重新显示时,需要重新创建Fragment实例并通过add()操作添加到Activity中。
(2)replace操作
通常我们对上面的代码做一些修改,注释掉之前的hide()操作,把show()改为replace(),具体修改如下:
if(currentTabIndex!=index)
{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// transaction.remove(fragments[currentTabIndex]);//替换之前的transaction.hide(fragments[currentTabIndex]);
if(!fragments[index].isAdded())
{
transaction.add(R.id.frame_container,fragments[index]);
}
transaction.replace(R.id.frame_containere,fragments[index]);//替换之前的show()方法
transaction.commit();
}
这里我们注释掉remove()方法,将show()方法替换为replace()方法,运行结果如下:
我们会发现,在调用replace()方法后,其他三个Fragment均销毁了。而销毁的Fragment需要重新显示时,需要重新创建对象,因此这里的开销还是相对比较大的。
5、总结
-
show() 用来在界面上呈现Fragment视图,相当于View的的setVisiable(View.Visiable
)。
-
hide() 用来隐藏Fragment视图,相当于View的setVisiable(View.InVisiable)
-
add() 用来向Activity中添加Fragment对象,但是需要注意已经添加的Fragment对象,不能重复添加,否则报错 java.lang.IllegalStateException: Fragment already added:
-
remove() 移除Activity中的添加过的Fragment对象,被移除的Fragment会被系统销毁。
-
replace() 调用replace方法,会移除其他Fragmen并销毁,只保留将要替代的Fragment对象。由于会销毁其他Fragment,因此开销较大。注意,我们要添加的Fragment对象,如果在执行replace之前没有添加到Activity,在执行replace后,则会被添加到当前的Activity中。(关于add和replace无法混用问题,后面会具体分析)
四、Fragment的回滚
回滚,从文字含义来讲,就是类似于我们使用git时的回退,我们可以看到我们的提交操作,也就是日志log,如果我们需要回退到某一次提交时,那么执行命令就可以了。
而这里我们的回滚操作也是类似的,我们在执行add()、replace()、hide()、show()、remove()操作时,均可以加入到回退栈中,便于执行回滚操作。
接下来我们通过实例来说明,上面的例子。
1、首先我们依次add了四个Fragment对象,然后分别加入回退栈并提交
具体操作如下:
(1)add Fragment_msg
(2)add Fragment_friend
(3)add Fragment_discover
(4)add Fragment_profile
getSupportFragmentManager().beginTransaction().replace(R.id.
frame_container
,
msg_fragment
)
;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction()
;
transaction.add(R.id.
frame_container
,
fragments
[
0
])
;
transaction.addToBackStack(
"msg"
)
;//加入到回退栈
transaction.commit
()
;
FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction()
;
transaction1.add(R.id.
frame_container
,
fragments
[
1
])
;
transaction1.addToBackStack(
"friend"
)
;//加入到回退栈
transaction1.commit
()
;
FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction()
;
transaction2.add(R.id.
frame_container
,
fragments
[
2
])
;
transaction2.addToBackStack(
"discover"
)
;//加入到回退栈
transaction2.commit
()
;
FragmentTransaction transaction3 = getSupportFragmentManager().beginTransaction()
;
transaction3.add(R.id.
frame_container
,
fragments
[
3
])
;//加入到回退栈
transaction3.addToBackStack(
"profile"
)
;
transaction3.commit
()
;
运行log如下:
可以看到四个Fragment均完成创建。
2、执行remove(fragment_friend),删除Fragment_fried
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.remove(fragments[index]);//这里的index对应fragmen_friend的索引
transaction.addToBackStack("hide"+index);//加入到回退栈
transaction.commit();
运行log如下:
可以看到,调用remove后,fragment_friend的视图销毁了,但是fragment_friend对象并没有销毁(原因自然是因为fragment_friend加入了回退),也就是说加入回退栈,则fragment对象不销毁,不加入回退栈,调用remove则fragment对象销毁。
3、执行remove(fragment_discover),删除Fragment_discover
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction()
;
transaction.remove(
fragments
[
index
])
;//这里的index对应fragmen_discover的索引
transaction.addToBackStack(
"hide"
+
index
)
;//加入到回退栈
transaction.commit();
运行log如下:
4、点击返回键,执行第一次回滚操作。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (event.getKeyCode())
{
case KeyEvent.KEYCODE_BACK:
FragmentManager manager = getSupportFragmentManager();
manager.popBackStack();//回退栈的栈顶出栈
return true;
}
return super.onKeyDown(keyCode, event);
}
我们预想,它应该回退到执行remove(fragment_discover)操作之前,那么结果如何呢?运行log如下:
我们会发现,fragment_discover重新创建视图,加入到Acitivty中,和我们预想的一致。
5、再次点击返回键,执行第二次回滚
我们预想,它应该回退到执行remove(fragment_friend)操作之前,那么结果如何呢?运行log如下:
我们会发现,fragment_friend重新创建视图,加入到Acitivty中,和我们预想的一致。
6、再次点击返回键,执行第三次回滚
我们预想,它应该回退到执行add(fragment_profile)操作之前,那么结果如何呢?运行log如下:
我们会发现,此时Fragment_profile销毁,Acitivity中只有Fragment_msg,Fragment_friend,,Fragment_discover
到这里我们也就对回滚操作有了一个大致的认识,那么就下来我们总结整理一下:
7、总结:
(1)回滚,类似于git的提交回退,我们可以利用回滚来回到之前的状态。
(2)执行remove()和replace()操作,如果不添加到回退栈,那么fragment视图和fragment对象均会销毁,如果添加到回退栈,则只销毁fragment视图,但是不销毁fragment对象。
(3)show、hide、remove、replace、add均可以执行回滚操作
五、补充:
1、关于app包下的Fragment和v4包下的Fragment的区别
(1)android.app.Fragment兼容最低版本为Android 3.0,而 android.support.v4.app.Fragment 兼容的最低版本为1.6
(2)获取FragmentManager的方式不同
v4包下的Fragment:通过 getSupportFragmentManager().获取FragmentManager
app包下的Fragment:通过 getFragmentManager()来获取FragmentManager
2、如何实现fragment的切换
这里我们推荐的解决方案是使用show+hide,这样可以避免fragment的销毁和重建的消耗
3、关于fragment的add和replace的混合使用问题
这个问题其实是谷歌的bug,主要原因是在与replace方法中在清空所有container中所有fragment实例时的问题,代码如下:
1. for (int i=0;i
2. Fragment old = mManager.mAdded.get(i);
3. ……
4. mManager.removeFragment(old, mTransition, mTransitionStyle);
5. }
这里们还是以上面为例子,此时我们add了四个fragment,然后执行replace()操作,则首先清空里面所有的fragment,分析这里的循环:
(1)i=0时,mManager.mAdded.size()=4,
-
mManager.mAdded.get(0)=Fragment_msg
-
mManager.mAdded.get(1)=Fragment_friend
-
mManager.mAdded.get(2)=Fragment_discover
-
mManager.mAdded.get(3)=Fragment_profile
此时mManager.mAdded.get(0)为Fragment_msg,则它被移除,移除后mManager.mAdded.size()=3,注意这里,循环的限制条件已经发生改变
(2)i=1时,mManager.mAdded.size()=3,
-
mManager.mAdded.get(0)=Fragment_friend
-
mManager.mAdded.get(1)=Fragment_discover
-
mManager.mAdded.get(2)=Fragment_profile
此时mManager.mAdded.get(1)为Fragment_discover,则将它移除,移除后mManager.mAdded.size()=2,注意这里,由于在执行第一次操作时,改变了mAdded.size(),因此第一次操作前mManager.mAdded.get(1)=Fragment_friend,而第二次操作前mManager.mAdded.get(1)=Fragment_discover,他们是不一样的,这样就导致Fragment_friend依然在Activity的Container中,没有被移除。、
这里也就解释了我们在使用add和replace时,即使执行了replace操作,仍然无法移除fragment。
但是这个bug,Google在Android 5.0开始就修复了,因此现在使用是正常的,但是还是需要考虑replace()销毁fragment带来的开销。