Fragment杂记

372 阅读3分钟

activity重建恢复导致的内存泄露

protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fragment_test);
    getSupportFragmentManager().beginTransaction().add(R.id.main_container, new Fragment()).commit();
}

该种方式直接添加fragment,在activity被系统重建恢复时(activity销毁时调用onSaveInstanceState存储已添加的fragment信息,activity重建时super.onCreate会调用到FragmentManager.restoreSaveState恢复销毁时的fragment)会导致fragment重复添加。最终会导致fragmentmanager中存在多个相同的脏fragment。

class FragmentManager
void restoreSaveState(@Nullable Parcelable state) {
    ...
    for (FragmentState fs : fms.mActive) {
        // 该行会调用fragmentFactory.instantiate(classLoader, fs.mClassName)创建fragment
        fragmentStateManager = new FragmentStateManager(mLifecycleCallbacksDispatcher,mFragmentStore,mHost.getContext().getClassLoader(),getFragmentFactory(), fs); 
        // 该行会添加已还原的fragment
        mFragmentStore.makeActive(fragmentStateManager);
    ...
}

正确添加方式

  1. findFragmentByTag查找历史添加是否为空
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fragment_test);
    if (getSupportFragmentManager().findFragmentByTag("fragment") != null) {
        getSupportFragmentManager().beginTransaction().add(R.id.main_container, new Fragment(), "fragment").commit();
    }
 }
  1. 使用replace替换添加
getSupportFragmentManager().beginTransaction().replace(R.id.main_container,  new Fragment()).commit();

fragment横切屏重建生命周期

fragment-life.drawio.png

commit

  1. commit
  2. commitAllowingStateLoss
  3. commitNow
  4. commitNowAllowingStateLoss

Fragment进行add、replace、remove、show、hide等操作是通过FragmentTransaction进行事务管理的。FragmentTransaction将多个事务操作通过commit进行一次性提交。

commit与commitAllowingStateLoss

commitAllowingStateLoss

Like commit but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.
与commit类似,但允许在保存activity state后执行提交。这很危险,因为如果稍后需要将activity从其状态恢复,则提交可能会丢失,因此这只适用于用户界面状态意外更改的情况。

也就是说activity调用onSaveInstanceState后使用commit提交fragment事务则会抛出IllegalStateException异常,而使用commitAllowingStateLoss进行事务添加则不会抛异常,然而该次事务会被丢弃。

commit与commitNow

commitNow

Commits this transaction synchronously. Any added fragments will be initialized and brought completely to the lifecycle state of their host and any removed fragments will be torn down accordingly before this call returns. Committing a transaction in this way allows fragments to be added as dedicated, encapsulated components that monitor the lifecycle state of their host while providing firmer ordering guarantees around when those fragments are fully initialized and ready. Fragments that manage views will have those views created and attached.
同步提交此事务。任何已添加的fragments都将被初始化,并完全进入其host的生命周期状态;任何被调用remove的fragments在return之前都会相应地被删除。通过这种方式提交事务,可以将fragment添加为专用的封装组件,以监控其host的生命周期状态,同时fragment完全初始化并准备就绪时提供更可靠的排序保证。管理View的fragment将创建并attatch这些view。

使用commit的时候,这个commit并不是立即执行的, 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行。而commitNow是同步立即执行的。commitNow不会将fragment添加到back-stack中。
commit之后调用 executePendingTransactions 实现立即同步提交,但executePendingTransactions会将所有pending在队列中和新提交的transactions都执行了, 而commitNow将只会执行当前要提交的transaction。