问题分析
一直在里看别人的技术贴,今天我也来写点自己的心得!最近在写一个项目用到大量的Fragment后的总结!
我想刚刚接触安卓的同学或许会这么写:
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.add(ViewId,fragment);// 或者fragmentTransaction.replace(ViewId,fragment);
fragmentTransaction.commit();
基础更好一点的同学会用show和hide方法
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.hide(new FirstFragment())
.show(new SecondFragment())
.commit();
诚然这两种都可以切换Fragment,但是面对用户大量点击来回切换,或者你的Fragment本来就很多,每次都这样操作,那么很快你的应用就会OOM,就算不崩那也会异常的卡顿!so why?
当我们replace时发生了以下的生命周期:
想想看每次都replace一下!!这世界会有多美好!!!那么问题出在哪?回过头看看代码就会发现每次在add/replace或者show/hide都会new 一个新的实例,这就是致命原因!!!!!
废话不多说,亮出我的方法(抽取后的):
/**
* Fragment的添加
* @param manager Fragment管理器
* @param aClass 相应的Fragment对象的getClass
* @param containerId 容器的id
* @param args 需要传值的话可将bundle填入 不需要传值就填null
*/
protected void addFragment(FragmentManager manager, Class extends BaseFragment> aClass, int containerId, Bundle args) {
String tag = aClass.getName();
Logger.d("%s add fragment %s", TAG, aClass.getSimpleName());
Fragment fragment = manager.findFragmentByTag(tag);
FragmentTransaction transaction = manager.beginTransaction(); // 开启一个事务
if (fragment == null) {// 没有添加
try {
fragment = aClass.newInstance(); // 通过反射 new 出一个 fragment 的实例
BaseFragment baseFragment = (BaseFragment) fragment; // 强转成我们base fragment
// 设置 fragment 进入,退出, 弹进,弹出的动画
transaction.setCustomAnimations(baseFragment.enter(), baseFragment.exit(), baseFragment.popEnter(), baseFragment.popExit());
transaction.add(containerId, fragment, tag);
if (baseFragment.isNeedToAddBackStack()) { // 判断是否需要加入回退栈
transaction.addToBackStack(tag); // 加入回退栈时制定一个tag,以便在找到指定的事务
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
if (fragment.isAdded()) {
if (fragment.isHidden()) {
transaction.show(fragment);
}
} else {
transaction.add(containerId, fragment, tag);
}
}
if (fragment != null) {
fragment.setArguments(args);
hideBeforeFragment(manager, transaction, fragment);
transaction.commit();
}
}
/**
* 除当前 fragment 以外的所有 fragment 进行隐藏
*
* @param manager
* @param transaction
* @param currentFragment
*/
private void hideBeforeFragment(FragmentManager manager, FragmentTransaction transaction, Fragment currentFragment) {
List fragments = manager.getFragments();
for (Fragment fragment : fragments) {
if (fragment != currentFragment && !fragment.isHidden()) {
transaction.hide(fragment);
}
}
}
略微解释一下:
先查询fragmentManager 所在的activitiy 中是否已经添加了这个fragment
第一步 先从一个mAdded 的一个ArrayList遍历查找,如果找不到再从 一个 叫 mActive 的 SparseArray的一个map里面查找。
注意:
1.一个 fragment 被 remove 掉后,只会从 mAdded 里面删除,不会从 mActive 里面删除,只有当这个fragment 所在的 transaction 从回退栈里面移除后才会 从mActive 删除
- 当我们add 一个fragment时 会把我们的fragment 添加到 mAdded 里面,不会添加到 mActive。
- 只有当我们把 transaction 添加到回退栈的时候,才会把我们的 fragment 添加到 mActive 里面。所以我们通过 findFragmentByTag 方法查找出来的 fragment 不一定是被添加到我们的 activity 中。
使用:
代码比较多,但是我个人感觉使用起来比较方便,而且功能也比较完善,使用的时候只需要两行代码:
HomeFragment1 homeFragment = new HomeFragment1();
addFragment(getSupportFragmentManager(),homeFragment.getClass(),R.id.main_body,null);
当我们需要传值的时候,只需要将准备好的bundle以参数的形式填入即可。
我还增加了一个是否加入回退栈的判断,用于实现一些有关回退栈的需求,实现这个功能还需要在BaseFragment中定义一个方法:
protected boolean isNeedToAddBackStack() {
return true;
}