美文网首页
Launcher3 中 StateManager 状态机

Launcher3 中 StateManager 状态机

作者: smart_dev | 来源:发表于2023-09-12 19:01 被阅读0次

概述

StateManager path:com.android.launcher3.statemanager.StateManager

核心状态控制类 ,基于不同状态为 StatefulActivity 管理不同状态之间转换的类

那状态机是怎么工作的?

首先我们要弄清楚一个点状态机是用来管理状态的,他要做的就是把每一种状态改变通知到相关方,并维护自己的状态值,即完成了一次状态管理;具体来说就是相关方在每一种状态下都有哪些组件有哪些变化。

StateHandler 接口所有实现

在进入正题之前,先看下StateHandler,即状态处理器,他是状态机和各个组件通信高度抽象。这里列出了所有的具体实现,后面分析会用到。

举例说明状态机原理

这里以在桌面拖动一个图标为例,拖动过程中状态是SpringLoaded, 即状态会从Normal 变成 SpringLoaded.

1. Workspace 会收到拖动事件

com.android.launcher3.Workspace#onDragStart

@Override
public void onDragStart(DragObject dragObject, DragOptions options) {
    ...
    // Always enter the spring loaded mode
    mLauncher.getStateManager().goToState(SPRING_LOADED);
    ...
}
  • 通过 mLauncher.getStateManager() 获取到StateManager ,此对象是在Launcher的Activity中创建

  • 然后调用 goToState 切换到 SPRING_LOADED

2. StateManager#goToState() 控制切换

com.android.launcher3.statemanager.StateManager#goToState(STATE_TYPE, boolean, long, android.animation.Animator.AnimatorListener)

StateManager 根据不同情况重载了多个 goToState() ,最终都会调用到参数最长的这个

private void goToState(
        STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
    animated &= areAnimatorsEnabled(); // 注释0

    // 注释1 
    if (mActivity.isInState(state)) {
        if (mConfig.currentAnimation == null) {
            // Run any queued runnable
            if (listener != null) {
                listener.onAnimationEnd(null);
            }
            return;
        } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
            // We are running the same animation as requested
            if (listener != null) {
                mConfig.currentAnimation.addListener(listener);
            }
            return;
        }
    }

    // 注释2
    // Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
    STATE_TYPE fromState = mState;
    cancelAnimation();

    // 注释3
    if (!animated) {
        mAtomicAnimationFactory.cancelAllStateElementAnimation();
        onStateTransitionStart(state); // 通知状态转换开始
        // 通知所有观察者
        for (StateHandler handler : getStateHandlers()) {
            handler.setState(state);
        }

        onStateTransitionEnd(state); // 通知状态转换完毕

        // Run any queued runnable
        if (listener != null) {
            listener.onAnimationEnd(null);
        }
        return;
    }

    // 注释4 
    if (delay > 0) {
        // Create the animation after the delay as some properties can change between preparing
        // the animation and running the animation.
        int startChangeId = mConfig.changeId;
        mUiHandler.postDelayed(() -> {
            if (mConfig.changeId == startChangeId) {
                goToStateAnimated(state, fromState, listener);
            }
        }, delay);
    } else {
        // 注释5
        goToStateAnimated(state, fromState, listener);
    }
}
  • 注释0:areAnimatorsEnabled() 实际上判断系统是否被禁用动画,默认true

  • 注释1 :判断要切换的目标状态,和当前activity的状态是否一致,实际上判断的是 mState == state

如果已经是这个状态,返回即可

  • 注释2,重置状态并取消动画

  • 注释3,如果不需要动画,会通过 handler.setState(state)来通知状态变化; 并更新状态

  • 注释4和注释5 实际区别就是是否有延迟,通过mUiHandler来执行延迟动作,最终都会执行需要动画的切换状态方法,即 goToStateAnimated()

那么我们注意到 注释3 会通过遍历所有 StateHandler进而来通知所有的观察者,那么都有哪些观察者呢?什么时候注册的?

3. StateManager#getStateHandlers() 获取观察者

getStateHandlers

com.android.launcher3.statemanager.StateManager#getStateHandlers

public StateHandler[] getStateHandlers() {
    if (mStateHandlers == null) {
        ArrayList<StateHandler> handlers = new ArrayList<>();
        mActivity.collectStateHandlers(handlers);
        mStateHandlers = handlers.toArray(new StateHandler[handlers.size()]);
    }
    return mStateHandlers;
}
  • 只获取一次,如果不为空直接就返回了

  • 为空的时候,通过调用activity的collectStateHandlers方法,并存如list集合中,最后转成数组返回

Launcher#collectStateHandlers

com.android.launcher3.Launcher#collectStateHandlers

@Override
protected void collectStateHandlers(List<StateHandler> out) {
    out.add(getAllAppsController());
    out.add(getWorkspace());
}
  • 添加观察者 AllAppsController

  • 添加观察者 Workspace

BaseQuickstepLauncher#collectStateHandlers

com.android.launcher3.BaseQuickstepLauncher#collectStateHandlers

@Override
protected void collectStateHandlers(List<StateHandler> out) {
    super.collectStateHandlers(out);
    out.add(getDepthController());
    out.add(new RecentsViewStateController(this));
    out.add(new BackButtonAlphaHandler(this));
    out.add(getTaskbarStateHandler());
}
  • 添加观察者 DepthController

  • 添加观察者 RecentsViewStateController

  • 添加观察者 BackButtonAlphaHandler

  • 添加观察者 TaskbarStateHandler

小结

观察者:launcher主activity会注册6个观察者,并且都实现接口 StateHanlder

被观察者:状态机对象本身,即StateManager

通知更新:根据是否需要动画调用 setStatesetStateWithAnimation 完成通知

4. StateManager#goToStateAnimated()

com.android.launcher3.statemanager.StateManager#goToStateAnimated

private void goToStateAnimated(STATE_TYPE state, STATE_TYPE fromState,
        AnimatorListener listener) {
    // 由于状态 mBaseState 可以从多个状态到达,
    //因此只需假设转换反向播放并使用与前一个状态相同的持续时间。
    mConfig.duration = state == mBaseState
            ? fromState.getTransitionDuration(mActivity)
            : state.getTransitionDuration(mActivity);
    // 注释1
    prepareForAtomicAnimation(fromState, state, mConfig);
    // 注释2
    AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
    if (listener != null) {
        animation.addListener(listener);
    }
    // 注释3
    mUiHandler.post(new StartAnimRunnable(animation));
}
  • 注释1:prepareForAtomicAnimation 准备从 fromState 到 toState 的非用户控制动画。准备工作包括:

    • 为状态转换中包含的各种动画设置插值器。

    • 为隐藏但即将显示的视图设置一些起始值(例如比例)。

  • 注释2:创建具体的动画并设置监听,来下文中 [createAnimationToNewWorkspaceInternal]

  • 注释3:使用 mUiHandler 执行动画集

5. StateManager#createAnimationToNewWorkspaceInternal

private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
    PendingAnimation builder = new PendingAnimation(mConfig.duration);
    if (!mConfig.hasAnimationFlag(SKIP_ALL_ANIMATIONS)) {
        for (StateHandler handler : getStateHandlers()) {
            handler.setStateWithAnimation(state, mConfig, builder);
        }
    }
    builder.addListener(createStateAnimationListener(state));
    mConfig.setAnimation(builder.buildAnim(), state);
    return builder;
}
  • 同样的通过 StateHandler#setStateWithAnimation来通知各个观察者完成动画的属性设置

  • 通过builder模式来聚合不同配置,进而构建不同的动画集合

  • 如果想看具体每个观察者都怎么设置的动画,就要去看各个实现了,这里不再讲述

总结

  1. 当需要切换状态时,会调用StateManager#goToState,并传入要到达的状态

  2. StateManager会收集所有的观察者StateHandler, 并通过setState或者setStateWithAnimation通知到所有的观察者做响应的变化

  3. 然后根据是否有动画来构造动画集,并调用UiHandler执行

  4. 最后StateManager同时会维护自己的本对象的几个状态值,便于管理切换和回退,以及通过StateListener来通知需要观察状态切换过开始和结束的对象

相关文章

网友评论

      本文标题:Launcher3 中 StateManager 状态机

      本文链接:https://www.haomeiwen.com/subject/laluvdtx.html