FragmentManager 源码浅谈:提线木偶控制台

122 阅读4分钟

想象 Android 系统是一个巨大的木偶剧场,FragmentManager 就是剧场的中央控制台,它通过复杂的线缆系统精确控制着每个木偶(Fragment)的一举一动。让我们揭开控制台的魔法面纱!

🎚️ 控制台核心结构

java

// 中央控制台的核心代码(简化版)
public class FragmentManager {
    // 所有木偶的登记册
    private ArrayList<Fragment> mFragments = new ArrayList<>();
    
    // 待执行操作队列
    private ArrayList<OpGenerator> mPendingActions = new ArrayList<>();
    
    // 历史记录卷轴(回退栈)
    private ArrayList<BackStackRecord> mBackStack;
    
    // 当前木偶状态机
    private int mCurState = Fragment.INITIALIZING;
    
    // 魔法方法:推动状态变化
    void moveToState(Fragment f, int newState) {
        // 根据状态执行木偶的生命周期方法
        switch(newState) {
            case Fragment.ATTACHED: 
                f.onAttach(); // "连接舞台电源!"
                break;
            case Fragment.CREATED:
                f.onCreate(); // "激活核心芯片!"
                break;
            // ...其他状态
        }
    }
}

🕹️ 木偶操作流程

1. 添加新木偶(添加 Fragment)

java

// 剧场管理员(Activity)的指令
getSupportFragmentManager().beginTransaction()
    .add(R.id.puppet_spot, new PuppetFragment(), "puppet1")
    .commit();

// 控制台内部执行:
public FragmentTransaction add(int containerId, Fragment fragment, String tag) {
    // 创建操作记录
    Op op = new Op(OP_ADD, fragment);
    op.mContainerId = containerId;
    op.mTag = tag;
    addOp(op); // 加入操作队列
    
    return this;
}

2. 执行操作(commit 魔法)

java

// 当调用 commit() 时
public int commit() {
    return commitInternal(true);
}

private int commitInternal(boolean allowStateLoss) {
    // 将操作加入待执行队列
    mPendingActions.add(this);
    
    // 安排在主线程执行
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

🧩 状态机引擎

控制台的核心是状态机引擎,它精确控制每个木偶的生命周期状态:

java

// 状态机推动方法
void moveToState(Fragment f, int newState) {
    // 状态升级路径
    if (f.mState < newState) {
        switch (f.mState) {
            case Fragment.INITIALIZING:
                if (newState >= Fragment.ATTACHED) {
                    attachFragment(f); // 连接舞台
                }
            case Fragment.ATTACHED:
                if (newState >= Fragment.CREATED) {
                    f.performCreate(f.mSavedFragmentState); // 创建核心
                }
            // ... 其他状态升级
        }
    }
    // 状态降级路径(如返回按钮)
    else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    f.performPause(); // 暂停表演
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    f.performStop(); // 停止表演
                }
            // ... 其他状态降级
        }
    }
}

📜 历史卷轴(回退栈)

当操作被添加到回退栈:

java

// 添加回退记录
transaction.addToBackStack("puppet_show");

// 控制台内部:
public FragmentTransaction addToBackStack(String name) {
    mAddToBackStack = true;
    mName = name;
    return this;
}

当用户按下返回键:

java

// 处理返回键
public boolean popBackStackImmediate() {
    // 从栈顶取出记录
    BackStackRecord bsr = mBackStack.remove(mBackStack.size()-1);
    
    // 执行反向操作
    bsr.popFromBackStack(true);
    return true;
}

⚡ 状态同步魔法

控制台如何确保所有木偶状态一致?

java

// 执行待处理操作
void execPendingActions() {
    while (true) {
        // 1. 合并所有待执行操作
        generateOpsForPendingActions(mTmpRecords, mTmpIsPop);
        
        // 2. 按优先级排序
        Collections.sort(mTmpRecords, mExecCommit);
        
        // 3. 依次执行操作
        for (BackStackRecord record : mTmpRecords) {
            record.executeOps();
        }
        
        // 4. 更新所有木偶状态
        moveToState(mCurState, true);
    }
}

🧠 木偶的保存与恢复

当剧场遭遇风暴(配置变更):

java

// 保存状态
public void saveAllState() {
    // 保存所有木偶状态
    ArrayList<FragmentState> fragments = new ArrayList<>();
    for (Fragment f : mFragments) {
        FragmentState fs = new FragmentState(f);
        fragments.add(fs);
    }
    
    // 保存回退栈
    BackStackState[] backStack = saveBackStack();
    
    // 打包成Bundle
    FragmentManagerState fms = new FragmentManagerState();
    fms.mActive = fragments;
    fms.mBackStack = backStack;
    return fms;
}

// 恢复状态
public void restoreAllState(Parcelable state) {
    FragmentManagerState fms = (FragmentManagerState)state;
    
    // 重建木偶列表
    for (FragmentState fs : fms.mActive) {
        Fragment f = fs.instantiate(...);
        mFragments.add(f);
    }
    
    // 重建回退栈...
}

🎯 控制流程图解

text

 操作请求 (add/remove/replace)
        ↓
 创建 BackStackRecord
        ↓
 加入 mPendingActions 队列
        ↓
 主线程 Handler 触发执行
        ↓
 execPendingActions() 批量处理
        ↓
 executeOps() 执行具体操作
        ↓
 moveToState() 更新状态
        ↓
 调用 Fragment 生命周期方法
        ↓
 更新视图层级

💡 控制台使用秘籍

  1. 线程安全法则

    java

    // 所有操作必须在主线程
    void updateFragment() {
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            throw new IllegalStateException("必须在主线程操作木偶!");
        }
    }
    
  2. 批量操作优化

    kotlin

    // 使用 beginTransaction().setReorderingAllowed(true)
    supportFragmentManager.commit {
        setReorderingAllowed(true)
        replace<FragmentA>(R.id.container)
        add<FragmentB>(R.id.sidebar)
        // 系统会智能合并操作
    }
    
  3. 查找木偶的正确方式

    java

    // 通过 ID 查找
    Fragment puppet = getSupportFragmentManager().findFragmentById(R.id.puppet_spot);
    
    // 通过标签查找(适用于无视图的 Fragment)
    Fragment puppet = getSupportFragmentManager().findFragmentByTag("audio_player");
    
  4. 安全操作检查

    java

    // 在操作前检查控制台状态
    if (!getSupportFragmentManager().isStateSaved()) {
        // 安全执行事务
    } else {
        // 状态已保存,操作将丢失
    }
    

终极测验:当同时提交 10 个 Fragment 事务时,控制台如何优化性能?

答案

  1. 将操作合并到单个消息循环处理
  2. 使用 setReorderingAllowed(true) 跳过中间状态
  3. 批量执行视图更新,减少布局次数
  4. 智能合并兼容操作(如多个 add 合并)

通过这个提线木偶控制台的揭秘,我们看到 FragmentManager 就像剧场的神经中枢,通过状态机引擎操作队列回退栈三大核心机制,精确控制着每个 Fragment 的生命周期。掌握这套控制系统的运作原理,你就能成为 Android 界面开发的王牌木偶师!