概述
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
通知更新:根据是否需要动画调用 setState
和 setStateWithAnimation
完成通知
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模式来聚合不同配置,进而构建不同的动画集合
如果想看具体每个观察者都怎么设置的动画,就要去看各个实现了,这里不再讲述
总结
当需要切换状态时,会调用
StateManager#goToState
,并传入要到达的状态StateManager
会收集所有的观察者StateHandler
, 并通过setState
或者setStateWithAnimation
通知到所有的观察者做响应的变化然后根据是否有动画来构造动画集,并调用
UiHandler
执行最后
StateManager
同时会维护自己的本对象的几个状态值,便于管理切换和回退,以及通过StateListener
来通知需要观察状态切换过开始和结束的对象