Android(安卓)-- StateMachine瑙f瀽
16lz
2021-01-24
Android -- StateMachine瑙f瀽
Android涓紝涓轰簡绠$悊Wifi鍚勪釜杩囩▼鐨勭姸鎬侊紝鎻愬嚭浜嗙姸鎬佹満锛屽嵆StateMachine鐨勬蹇点€備娇鐢⊿tateMachine锛屽彲浠ュ皢绻佹潅鐨勮繃绋嬬粏鍖栦负涓€涓釜鍒嗘敮鐘舵€侊紝鏄撲簬绠$悊锛屽苟涓斾唬鐮佺殑缁撴瀯涔熸洿娓呮櫚锛屾槗浜庨槄璇汇€俉ifiStateMachine.java鏂囦欢灏卞畾涔変簡杩欐牱涓€涓粨鏋勫寲鐘舵€佹満锛屽畠绠$悊Wifi椹卞姩鍔犺浇銆佽繛鎺ャ€佹壂鎻忎互鍙婃柇寮€绛夊悇涓姸鎬併€傛湰鏂囧氨浠ifiStateMachine涓哄叆鍙f潵绠€瑕佸垎鏋怱tateMachine(WiFiController瀹為檯涓婁篃瀹氫箟浜嗕竴涓粨鏋勫寲鐘舵€佹満)鐨勫疄鐜板師鐞嗐€? 杩欓噷璇寸殑姣忎竴涓猻tate閮芥槸涓€涓猄tate绫诲璞★紝瀹冨繀椤诲疄鐜皃rocessMessage鏂规硶锛屼絾enter/exit/getName鏂规硶鏄惁瀹炵幇鏄彲閫夌殑锛岃繖瀹屽叏鍙栧喅浜庤璁¤€呯殑闇€瑕併€俥nter/exit鏂规硶鐩稿綋浜庨潰鍚戝璞$紪绋嬩腑鐨勬瀯閫犲拰鏋愭瀯杩囩▼锛屽畠浠粡甯歌鐢ㄤ簬鎵ц鏌愪釜state鐨勭浉鍏冲垵濮嬪寲鍜屾竻鐞嗗伐浣溿€俫etName()鏂规硶涓€鑸敤鏉ヨ繑鍥炴煇涓猻tate鐨勫悕瀛椼€? State绫荤殑鍏蜂綋瀹炵幇濡備笅锛?
/** * {@hide} * * The class for implementing states in a StateMachine */public class State implements IState { /** * Constructor */ protected State() { } /* (non-Javadoc) * @see com.android.internal.util.IState#enter() */ @Override public void enter() { } /* (non-Javadoc) * @see com.android.internal.util.IState#exit() */ @Override public void exit() { } /* (non-Javadoc) * @see com.android.internal.util.IState#processMessage(android.os.Message) */ @Override public boolean processMessage(Message msg) { return false; } /** * Name of State for debugging purposes. * * This default implementation returns the class name, returning * the instance name would better in cases where a State class * is used for multiple states. But normally there is one class per * state and the class name is sufficient and easy to get. You may * want to provide a setName or some other mechanism for setting * another name if the class name is not appropriate. * * @see com.android.internal.util.IState#processMessage(android.os.Message) */ @Override public String getName() { String name = getClass().getName(); int lastDollar = name.lastIndexOf('$'); return name.substring(lastDollar + 1); }
杩欓噷鐨勭粨鏋勫寲鐘舵€佹満瀹為檯涓婃槸涓€涓爲鐘剁粨鏋勩€傚綋鎴戜滑鏋勫缓涓€涓姸鎬佹満鏃讹紝璋冪敤addState()鏂规硶鍚戠姸鎬佹満涓坊鍔爏tate锛岃鏂规硶浼氬缓绔嬪悇涓猻tate涔嬮棿鐨勭粨鏋勫寲鐗规€э紱璋冪敤setInitialState()缁欏綋鍓嶇殑鐘舵€佹満缁撴瀯瀹氫箟涓€涓垵濮嬪寲鐘舵€侊紱鐘舵€佹満鏋勯€犲伐浣滃畬鎴愬悗锛岃皟鐢╯tart()鏂规硶鏉ュ紑鍚鐘舵€佹満銆? 涓嬮潰鎴戜滑閫氳繃WifiStateMachine鐘舵€佹満鐨勬瀯寤鸿繃绋嬶紝鏉ヨВ鏋怱tateMachine鐨勫垱寤鸿繃绋嬨€?
WifiStateMachine鐨勫畾涔夊涓嬶細
public class WifiStateMachine extends StateMachine implements WifiNative.WifiPnoEventHandler {...}
鍏舵瀯閫犲嚱鏁扮殑涓昏閮ㄥ垎锛? public WifiStateMachine(Context context, String wlanInterface,WifiTrafficPoller trafficPoller) { ... addState(mDefaultState); addState(mInitialState, mDefaultState); addState(mSupplicantStartingState, mDefaultState); addState(mSupplicantStartedState, mDefaultState); addState(mDriverStartingState, mSupplicantStartedState); addState(mDriverStartedState, mSupplicantStartedState); addState(mScanModeState, mDriverStartedState); addState(mConnectModeState, mDriverStartedState); addState(mL2ConnectedState, mConnectModeState); addState(mObtainingIpState, mL2ConnectedState); addState(mVerifyingLinkState, mL2ConnectedState); addState(mConnectedState, mL2ConnectedState); addState(mRoamingState, mL2ConnectedState); addState(mDisconnectingState, mConnectModeState); addState(mDisconnectedState, mConnectModeState); addState(mWpsRunningState, mConnectModeState); addState(mWaitForP2pDisableState, mSupplicantStartedState); addState(mDriverStoppingState, mSupplicantStartedState); addState(mDriverStoppedState, mSupplicantStartedState); addState(mSupplicantStoppingState, mDefaultState); addState(mSoftApStartingState, mDefaultState); addState(mSoftApStartedState, mDefaultState); addState(mTetheringState, mSoftApStartedState); addState(mTetheredState, mSoftApStartedState); addState(mUntetheringState, mSoftApStartedState); setInitialState(mInitialState); setLogRecSize(ActivityManager.isLowRamDeviceStatic() ? 100 : 3000); setLogOnlyTransitions(false); if (VDBG) setDbg(true); //start the state machine start(); ...}
鐢变唬鐮佸彲鐭ワ紝涓烘瀯寤轰竴涓姸鎬佹満锛屾垜浠渶瑕佷笁涓楠わ細 - 鍒涘缓鏌愪簺缁ф壙鑷猄tate鐨勭被锛屼唬琛ㄦ垜浠娣诲姞鐨勭姸鎬?/li>
- 璋冪敤addState()鏂规硶锛屾坊鍔犵姸鎬佹満
- 璋冪敤setInitialState()璁剧疆鍒濆鐘舵€?/li>
- 璋冪敤start()鏂规硶锛屽紑鍚姸鎬佹満
class InitialState extends State { @Override public void enter() { WifiNative.stopHal(); mWifiNative.unloadDriver(); if (mWifiP2pChannel == null) { mWifiP2pChannel = new AsyncChannel(); mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pServiceImpl.getP2pStateMachineMessenger()); } if (mWifiApConfigChannel == null) { mWifiApConfigChannel = new AsyncChannel(); mWifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore( mContext, getHandler()); mWifiApConfigStore.loadApConfiguration(); mWifiApConfigChannel.connectSync(mContext, getHandler(), mWifiApConfigStore.getMessenger()); } if (mWifiConfigStore.enableHalBasedPno.get()) { // make sure developer Settings are in sync with the config option mHalBasedPnoEnableInDevSettings = true; } } @Override public boolean processMessage(Message message) { logStateAndMessage(message, getClass().getSimpleName()); switch (message.what) { case CMD_START_SUPPLICANT: if (mWifiNative.loadDriver()) { try { mNwService.wifiFirmwareReload(mInterfaceName, "STA"); } catch (Exception e) { loge("Failed to reload STA firmware " + e); // Continue } try { // A runtime crash can leave the interface up and // IP addresses configured, and this affects // connectivity when supplicant starts up. // Ensure interface is down and we have no IP // addresses before a supplicant start. mNwService.setInterfaceDown(mInterfaceName); mNwService.clearInterfaceAddresses(mInterfaceName); // Set privacy extensions mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); // IPv6 is enabled only as long as access point is connected since: // - IPv6 addresses and routes stick around after disconnection // - kernel is unaware when connected and fails to start IPv6 negotiation // - kernel can start autoconfiguration when 802.1x is not complete mNwService.disableIpv6(mInterfaceName); } catch (RemoteException re) { loge("Unable to change interface settings: " + re); } catch (IllegalStateException ie) { loge("Unable to change interface settings: " + ie); } /* Stop a running supplicant after a runtime restart * Avoids issues with drivers that do not handle interface down * on a running supplicant properly. */ mWifiMonitor.killSupplicant(mP2pSupported); if (WifiNative.startHal() == false) { /* starting HAL is optional */ loge("Failed to start HAL"); } if (mWifiNative.startSupplicant(mP2pSupported)) { setWifiState(WIFI_STATE_ENABLING); if (DBG) log("Supplicant start successful"); mWifiMonitor.startMonitoring(); transitionTo(mSupplicantStartingState); } else { loge("Failed to start supplicant!"); } } else { loge("Failed to load driver"); } break; case CMD_START_AP: if (mWifiNative.loadDriver() == false) { loge("Failed to load driver for softap"); } else { if (enableSoftAp() == true) { setWifiApState(WIFI_AP_STATE_ENABLING, 0); transitionTo(mSoftApStartingState); } else { setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); transitionTo(mInitialState); } } break; default: return NOT_HANDLED; } return HANDLED; } }
杩欐槸WifiStateMachine涓璉nitialState绫荤殑瀹炵幇銆? 涓嬮潰鎴戜滑鐪媋ddState()鏂规硶銆俉ifiStateMachine缁ф壙鑷猄tateMachine锛岃皟鐢╝ddState()瀹為檯涓婃槸璋冪敤鐖剁被涓殑鍚屽悕鏂规硶锛? /** * Add a new state to the state machine, parent will be null * @param state to add */ protected final void addState(State state) { mSmHandler.addState(state, null); }
灞傜骇璋冪敤SmHandler绫荤殑鏂规硶锛? /** * Add a new state to the state machine. Bottom up addition * of states is allowed but the same state may only exist * in one hierarchy. * * @param state the state to add * @param parent the parent of state * @return stateInfo for this state */ private final StateInfo addState(State state, State parent) { if (mDbg) { mSm.log("addStateInternal: E state=" + state.getName() + ",parent=" + ((parent == null) ? "" : parent.getName())); } StateInfo parentStateInfo = null; if (parent != null) { parentStateInfo = mStateInfo.get(parent); if (parentStateInfo == null) { // Recursively add our parent as it's not been added yet. parentStateInfo = addState(parent, null); } } StateInfo stateInfo = mStateInfo.get(state); if (stateInfo == null) { stateInfo = new StateInfo(); mStateInfo.put(state, stateInfo); } // Validate that we aren't adding the same state in two different hierarchies. if ((stateInfo.parentStateInfo != null) && (stateInfo.parentStateInfo != parentStateInfo)) { throw new RuntimeException("state already added"); } stateInfo.state = state; stateInfo.parentStateInfo = parentStateInfo; stateInfo.active = false; if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo); return stateInfo; }
SmHandler鏄疭tateMachine鍐呭疄鐜扮殑涓€涓狧andler瀛愮被锛屽畠绠$悊鐫€鐘舵€佹満鐘舵€佺殑娣诲姞銆佸垏鎹㈠強娑堟伅鍙戦€佺瓑閲嶈鍔熻兘銆係mHandler姣旇緝閲嶈鐨勬垚鍛樺彉閲忥細 /**true if StateMachine has quit */ private boolean mHasQuit = false; /** The debug flag */ private boolean mDbg = false; /** The SmHandler object, identifies that message is internal */ private static final Object mSmHandlerObj = new Object(); /** The current message */ private Message mMsg; /** A list of log records including messages this state machine has processed */ private LogRecords mLogRecords = new LogRecords(); /** true if construction of the state machine has not been completed */ private boolean mIsConstructionCompleted; /** Stack used to manage the current hierarchy of states */ private StateInfo mStateStack[]; /** Top of mStateStack */ private int mStateStackTopIndex = -1; /** A temporary stack used to manage the state stack */ private StateInfo mTempStateStack[]; /** The top of the mTempStateStack */ private int mTempStateStackCount; /** State used when state machine is halted */ private HaltingState mHaltingState = new HaltingState(); /** State used when state machine is quitting */ private QuittingState mQuittingState = new QuittingState(); /** Reference to the StateMachine */ private StateMachine mSm; /** * Information about a state. * Used to maintain the hierarchy. */ private class StateInfo { /** The state */ State state; /** The parent of this state, null if there is no parent */ StateInfo parentStateInfo; /** True when the state has been entered and on the stack */ boolean active; /** * Convert StateInfo to string */ @Override public String toString() { return "state=" + state.getName() + ",active=" + active + ",parent=" + ((parentStateInfo == null) ? "null" : parentStateInfo.state.getName()); } } /** The map of all of the states in the state machine */ private HashMap mStateInfo = new HashMap(); /** The initial state that will process the first message */ private State mInitialState; /** The destination state when transitionTo has been invoked */ private State mDestState;
鏍规嵁娉ㄩ噴鎴戜滑鍙煡瀹冧滑浠h〃鐨勫惈涔夛紝杩欓噷灏变笉鍦ㄨ禈杩般€? 鎴戜滑鍒嗘瀽addState()鐨勫疄鐜帮紝WifiStateMachine涓姸鎬佹満鏁伴噺澶氥€佸お澶嶆潅銆備负鍒嗘瀽鏋勫缓杩囩▼锛屾垜浠互婧愮爜涓彁渚涚殑涓€涓緥瀛愪綔涓烘湰鏂囩殑绀轰緥锛? 聽 聽 聽 聽 聽 聽mP0 聽 聽 聽 聽 聽/ 聽 聽 聽 聽 \
聽 聽 聽 聽 mP1 聽 mS0
聽 聽 聽 聽/ 聽 聽 聽 \
聽 聽 聽 mS2 聽 mS1
聽 聽 聽/ 聽 聽 聽 聽\ 聽 聽 聽 聽\
聽 聽 mS3 聽mS4 聽mS5 聽---> initial state
鍒涘缓浠g爜涓猴細
addState(mP0);addState(mP1, mP0);addState(mS2, mP1);addState(mS3, mS2);addState(mS4, mS2);addState(mS1, mP1);addState(mS5, mP1);addState(mS0, mP0);setInitialState(mS5);start();
鍦╝ddState()鏂规硶涓紝棣栧厛鍒ゆ柇鍔犲叆鐨剆tate鐨勭埗state鏄惁涓虹┖锛涗笉涓虹┖锛屾帴鐫€纭畾鐖秙tate鏄惁宸茬粡娣诲姞鍒板眰娆$粨鏋勪腑锛屽鏋滄病鏈夊垯閫掑綊璋冪敤addState()鏂规硶锛屾坊鍔犵埗state锛涗负绌猴紝鍒欒鏄庤繖涓猻tate鏄暣涓姸鎬佹満鏍戠粨鏋勭殑椤剁偣锛屽垱寤哄畠鐨剆tateInfo瀵硅薄锛屾坊鍔犲埌mStateInfo杩欎釜HashMap缁撴瀯涓紝瀹冧繚鎸佷簡鐘舵€佹満鐨勫眰娆$粨鏋勬€с€係tateInfo鐨刟ctive灞炴€ф爣绀鸿state鐨別nter鏂规硶鏄惁琚墽琛屻€? 姣忎釜StateInfo閮戒繚瀛樹簡褰撳墠鐨勭姸鎬佸拰瀹冪殑鐖剁姸鎬佷俊鎭€傛垜浠粠mStateInfo鎴彇mP0鍒癿S2鐨勭姸鎬佷俊鎭潵鍒嗘瀽瀹冩€庝箞淇濇寔鍚勪釜鐘舵€佷箣闂寸殑鑱旂郴鐨勶細 Stae | StateInfo |
---|---|
聽 聽聽mP0 | state = mP0, parentStateInfo 娌℃湁淇℃伅锛屾牴鑺傜偣 |
聽 聽聽mP1 | state = mP1, parentStateinfo = mP0鐨凷tateInfo |
聽 聽聽mS2 | state = mS2, parenetStateInfo = mP1鐨凷tateInfo |
protected final void setInitialState(State initialState) { mSmHandler.setInitialState(initialState); }
闂存帴璋冪敤SmHandler涓殑鍚屽悕鏂规硶锛? /** @see StateMachine#setInitialState(State) */ private final void setInitialState(State initialState) { if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName()); mInitialState = initialState; }
鎶婅缃殑鍒濆鐘舵€佷繚瀛樺湪SmHandler鐨勪竴涓垚鍛樺彉閲忎腑銆傛渶鍚庡氨鏄皟鐢╯tart()鏂规硶锛屽紑鍚姸鎬佹満锛? public void start() { // mSmHandler can be null if the state machine has quit. SmHandler smh = mSmHandler; if (smh == null) return; /** Send the complete construction message */ smh.completeConstruction(); }
/** * Complete the construction of the state machine. */ private final void completeConstruction() { if (mDbg) mSm.log("completeConstruction: E"); /** * Determine the maximum depth of the state hierarchy * so we can allocate the state stacks. */ int maxDepth = 0; for (StateInfo si : mStateInfo.values()) { int depth = 0; for (StateInfo i = si; i != null; depth++) { i = i.parentStateInfo; } if (maxDepth < depth) { maxDepth = depth; } } if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth); mStateStack = new StateInfo[maxDepth]; mTempStateStack = new StateInfo[maxDepth]; setupInitialStateStack(); /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */ sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)); if (mDbg) mSm.log("completeConstruction: X"); }
鏂规硶棣栧厛璁$畻浜嗚灞傛鐘舵€佹満鐨勬渶澶ф繁搴?鏍戠姸缁撴瀯)锛涘苟鍒涘缓浜嗕袱涓暟缁勶紝鐢ㄤ簬淇濆瓨鐘舵€佹満杩愯涓殑鐘舵€併€傛帴鐫€璋冪敤锛? /** * Initialize StateStack to mInitialState. */ private final void setupInitialStateStack() { if (mDbg) { mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName()); } StateInfo curStateInfo = mStateInfo.get(mInitialState); for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) { mTempStateStack[mTempStateStackCount] = curStateInfo; curStateInfo = curStateInfo.parentStateInfo; } // Empty the StateStack mStateStackTopIndex = -1; moveTempStateStackToStateStack(); }
璇ユ柟娉曟妸浠庡垵濮嬭妭鐐瑰紑濮嬬殑鍚勪釜鐘舵€佺殑StateInfo淇濆瓨鍦╩TempStateStack鏁扮粍涓紱鍐嶈皟鐢細 /** * Move the contents of the temporary stack to the state stack * reversing the order of the items on the temporary stack as * they are moved. * * @return index into mStateStack where entering needs to start */ private final int moveTempStateStackToStateStack() { int startingIndex = mStateStackTopIndex + 1; int i = mTempStateStackCount - 1; int j = startingIndex; while (i >= 0) { if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j); mStateStack[j] = mTempStateStack[i]; j += 1; i -= 1; } mStateStackTopIndex = j - 1; if (mDbg) { mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex + ",startingIndex=" + startingIndex + ",Top=" + mStateStack[mStateStackTopIndex].state.getName()); } return startingIndex; }
璇ユ柟娉曚富瑕佹妸mTempStateStack鏁扮粍涓繚瀛樼殑淇℃伅閫嗗簭鍐嶄繚瀛樺埌mStateStack鏁扮粍涓€? 鍦ㄦ垜浠繖涓ず渚嬩腑锛? mTempStateStack[ ]鏁扮粍鐨勫唴瀹规槸锛歮S5_StateInfo, mS1_StateInfo, mP1_StateInfo, mP0_StateInfo. mStateStack[ ]鏁扮粍鐨勫唴瀹规槸锛歮P0_StateInfo, mP1_StateInfo, mS1_StateInfo, mS5_StateInfo. 鍥炲埌completeConstruction()鏂规硶涓紝鏈€鍚庤皟鐢╯endMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj))鍚慡mHandler鍙戦€佷竴涓秷鎭紝澶勭悊濡備笅锛?
/** * Handle messages sent to the state machine by calling * the current state's processMessage. It also handles * the enter/exit calls and placing any deferred messages * back onto the queue when transitioning to a new state. */ @Override public final void handleMessage(Message msg) { if (!mHasQuit) { if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what); /** Save the current message */ mMsg = msg; /** State that processed the message */ State msgProcessedState = null; if (mIsConstructionCompleted) { /** Normal path */ msgProcessedState = processMsg(msg); } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) { /** Initial one time path. */ mIsConstructionCompleted = true; invokeEnterMethods(0); } else { throw new RuntimeException("StateMachine.handleMessage: " + "The start method not called, received msg: " + msg); } performTransitions(msgProcessedState, msg); // We need to check if mSm == null here as we could be quitting. if (mDbg && mSm != null) mSm.log("handleMessage: X"); } }
濡傛灉鏄涓€娆″垵濮嬪寲鐘舵€佹満锛岃繘鍏lse if鍒嗘敮锛岃皟鐢╥nvokeEnterMethods锛? /** * Invoke the enter method starting at the entering index to top of state stack */ private final void invokeEnterMethods(int stateStackEnteringIndex) { for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); mStateStack[i].state.enter(); mStateStack[i].active = true; } }
鎸塵StateStack鏁扮粍涓繚瀛樼殑鐘舵€佹満椤哄簭锛屾寜浠庝笂鍒颁笅鐨勫眰娆$粨鏋勪緷娆¤皟鐢ㄥ悇涓猄tate鐨別nter()鏂规硶锛屽苟灏嗘瘡涓猄tate鐨刟ctive鏍囧織璁句负true銆傚埌杩欓噷锛屼竴涓姸鎬佹満鐨勬瀯寤猴紝鍖呮嫭娣诲姞鐘舵€併€佽缃垵濮嬬姸鎬佷互鍙婂紑鍚姸鎬佹満鐨勮繃绋嬪氨缁撴潫浜嗐€? 鏈€鍚庡啀浠嬬粛涓€涓嬬姸鎬佹満鐨勮浆鎹㈣繃绋嬨€? 鍦╓ifiStateMachine涓紝鐘舵€佹満鐨勭姸鎬佸垏鎹㈡槸璋冪敤transitionTo()鏂规硶锛岄棿鎺ヨ皟鐢⊿mHandler鍐呴儴鐨勫悓鍚嶆柟娉曪細 /** * transition to destination state. Upon returning * from processMessage the current state's exit will * be executed and upon the next message arriving * destState.enter will be invoked. * * this function can also be called inside the enter function of the * previous transition target, but the behavior is undefined when it is * called mid-way through a previous transition (for example, calling this * in the enter() routine of a intermediate node when the current transition * target is one of the nodes descendants). * * @param destState will be the state that receives the next message. */ protected final void transitionTo(IState destState) { mSmHandler.transitionTo(destState); }
/** @see StateMachine#transitionTo(IState) */ private final void transitionTo(IState destState) { mDestState = (State) destState; if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName()); }
鍦ㄥ鐞哠M_INIT_CMD娑堟伅鏃讹紝鍙戠幇杩樿皟鐢ㄤ簡performTransitions(msgProcessedState, msg);鐘舵€佹満鍚勪釜鐘舵€佺殑鍒囨崲灏辨槸鍦ㄨ繖涓嚱鏁颁腑澶勭悊鐨勩€? 聽 聽 聽 聽 聽 聽mP0
聽 聽 聽 聽 聽/ 聽 聽 聽 聽 \
聽 聽 聽 聽 mP1 聽 mS0
聽 聽 聽 聽/ 聽 聽 聽 \
聽 聽 聽 mS2 聽 mS1
聽 聽 聽/ 聽 聽 聽 聽\ 聽 聽 聽 聽\
聽 聽 mS3 聽mS4 聽mS5 聽---> initial state
涓轰簡鏂逛究鍒嗘瀽锛岀幇鍦ㄥ亣璁炬垜浠灏嗙姸鎬佸垏鎹㈠埌mS4锛歵ransitionTo(mS4)锛屾潵鐪嬬湅杩欎釜杩囩▼浼氬仛鍝簺閲嶈鐨勫鐞嗗伐浣溿€? 棣栧厛锛屼細鎶婂皢瑕佽鎹㈢殑鐘舵€佷繚瀛樺湪SmHandler鐨刴DestState鎴愬憳鍙橀噺涓紝杩欒〃绀烘垜浠殑鐩殑鐘舵€佹槸mS4銆傜湅锛?
/** * Do any transitions * @param msgProcessedState is the state that processed the message */ private void performTransitions(State msgProcessedState, Message msg) { /** * If transitionTo has been called, exit and then enter * the appropriate states. We loop on this to allow * enter and exit methods to use transitionTo. */ State orgState = mStateStack[mStateStackTopIndex].state; /** * Record whether message needs to be logged before we transition and * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which * always set msg.obj to the handler. */ boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj); if (mLogRecords.logOnlyTransitions()) { /** Record only if there is a transition */ if (mDestState != null) { mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState, mDestState); } } else if (recordLogMsg) { /** Record message */ mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState, mDestState); } State destState = mDestState; if (destState != null) { /** * Process the transitions including transitions in the enter/exit methods */ while (true) { if (mDbg) mSm.log("handleMessage: new destination call exit/enter"); /** * Determine the states to exit and enter and return the * common ancestor state of the enter/exit states. Then * invoke the exit methods then the enter methods. */ StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); invokeExitMethods(commonStateInfo); int stateStackEnteringIndex = moveTempStateStackToStateStack(); invokeEnterMethods(stateStackEnteringIndex); /** * Since we have transitioned to a new state we need to have * any deferred messages moved to the front of the message queue * so they will be processed before any other messages in the * message queue. */ moveDeferredMessageAtFrontOfQueue(); if (destState != mDestState) { // A new mDestState so continue looping destState = mDestState; } else { // No change in mDestState so we're done break; } } mDestState = null; } /** * After processing all transitions check and * see if the last transition was to quit or halt. */ if (destState != null) { if (destState == mQuittingState) { /** * Call onQuitting to let subclasses cleanup. */ mSm.onQuitting(); cleanupAfterQuitting(); } else if (destState == mHaltingState) { /** * Call onHalting() if we've transitioned to the halting * state. All subsequent messages will be processed in * in the halting state which invokes haltedProcessMessage(msg); */ mSm.onHalting(); } } }
澶勭悊鐨勪富瑕佽繃绋嬪湪while寰幆涓紝棣栧厛璋冪敤setupTempStateStackWithStatesToEnter()鏂规硶锛? /** * Setup the mTempStateStack with the states we are going to enter. * * This is found by searching up the destState's ancestors for a * state that is already active i.e. StateInfo.active == true. * The destStae and all of its inactive parents will be on the * TempStateStack as the list of states to enter. * * @return StateInfo of the common ancestor for the destState and * current state or null if there is no common parent. */ private final StateInfo setupTempStateStackWithStatesToEnter(State destState) { /** * Search up the parent list of the destination state for an active * state. Use a do while() loop as the destState must always be entered * even if it is active. This can happen if we are exiting/entering * the current state. */ mTempStateStackCount = 0; StateInfo curStateInfo = mStateInfo.get(destState); do { mTempStateStack[mTempStateStackCount++] = curStateInfo; curStateInfo = curStateInfo.parentStateInfo; } while ((curStateInfo != null) && !curStateInfo.active); if (mDbg) { mSm.log("setupTempStateStackWithStatesToEnter: X mTempStateStackCount=" + mTempStateStackCount + ",curStateInfo: " + curStateInfo); } return curStateInfo; }
璇ュ嚱鏁颁富瑕佹槸閲嶆柊璁剧疆mTempStateStack()鏁扮粍锛屽湪鐘舵€佹満灞傛缁撴瀯涓紝鏌ヨ鐩爣鐘舵€佸眰娆$粨鏋勪箣涓婄殑鎵€鏈夌埗绫荤姸鎬侊紝灏嗛偅浜涙病鏈夎皟鐢ㄨ繃鍏秂nter()鏂规硶鐨勭埗鐘舵€佷繚瀛樺湪鏁扮粍涓紝鏌ヨ渚濇嵁灏辨槸姣忎釜鐘舵€佺殑StateInfo鐨刟ctive灞炴€э紝涓篺alse灏辨槸娌¤璋冪敤杩囥€傝繖涓ず渚嬩腑mTempStateStack[ ]鏁扮粍闇€瑕佹洿鏂扮殑鐘舵€侊細mS4_StateInfo, mS2_StateInfo銆? 鎺ョ潃璋冪敤invokeExitMethods()鍑芥暟锛? /** * Call the exit method for each state from the top of stack * up to the common ancestor state. */ private final void invokeExitMethods(StateInfo commonStateInfo) { while ((mStateStackTopIndex >= 0) && (mStateStack[mStateStackTopIndex] != commonStateInfo)) { State curState = mStateStack[mStateStackTopIndex].state; if (mDbg) mSm.log("invokeExitMethods: " + curState.getName()); curState.exit(); mStateStack[mStateStackTopIndex].active = false; mStateStackTopIndex -= 1; } }
姝ゅ浼氭寜鍘熷厛鐨劼爉StateStack[ ]鏁扮粍涓繚瀛樼殑鍚勪釜鐘舵€佺殑椤哄簭渚濇鍏堣皟鐢ㄥ垵濮嬭妭鐐瑰強鍏舵墍鏈夌埗鑺傜偣鐨別xit()鏂规硶锛屽皢active璁剧疆涓篺alse锛涜繖涓繃绋嬬洿鍒伴亶鍘嗙殑鑺傜偣鏄垵濮嬭妭鐐瑰拰鐩殑鑺傜偣鐨勫叕鍏辩埗鑺傜偣鏂规墠鍋滄銆? 鎺ョ潃璋冪敤moveTempStateStackToStateStack()鍑芥暟锛? /** * Move the contents of the temporary stack to the state stack * reversing the order of the items on the temporary stack as * they are moved. * * @return index into mStateStack where entering needs to start */ private final int moveTempStateStackToStateStack() { int startingIndex = mStateStackTopIndex + 1; int i = mTempStateStackCount - 1; int j = startingIndex; while (i >= 0) { if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j); mStateStack[j] = mTempStateStack[i]; j += 1; i -= 1; } mStateStackTopIndex = j - 1; if (mDbg) { mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex + ",startingIndex=" + startingIndex + ",Top=" + mStateStack[mStateStackTopIndex].state.getName()); } return startingIndex; }
灏唌TempStateStack[ ]鏁扮粍涓繚瀛樼殑鐘舵€佷俊鎭€嗗簭鍐嶅瓨鍏StateStack[ ]鏁扮粍涓€? 鎺ョ潃璋冪敤invokeEnterMethods()鍑芥暟锛? /** * Invoke the enter method starting at the entering index to top of state stack */ private final void invokeEnterMethods(int stateStackEnteringIndex) { for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); mStateStack[i].state.enter(); mStateStack[i].active = true; } }
鍐嶆寜鐓StateStack[ ]鏁扮粍涓瓨鍌ㄧ殑椤哄簭渚濇璋冪敤鍚勪釜鐘舵€佺殑enter()鏂规硶锛屽苟灏哸ctive灞炴€ц缃负true銆傚湪鍒囨崲鐨勬渶鍚庯紝杩樹細鎶婅鐘舵€佹満寤惰繜澶勭悊鐨勬秷鎭斁鍒版秷鎭槦鍒椾腑鐨勬渶鍓嶅垪锛岃鍒囨崲鍚庣殑鏂扮姸鎬佷紭鍏堝鐞嗐€傝嚦姝ょ姸鎬佸垏鎹㈢殑杩囩▼鍩烘湰瀹屾瘯銆? 鐘舵€佹満澶勭悊娑堟伅鐨勫嚱鏁版槸SmHandler::processMsg()鏂规硶锛? /** * Handle messages sent to the state machine by calling * the current state's processMessage. It also handles * the enter/exit calls and placing any deferred messages * back onto the queue when transitioning to a new state. */ @Override public final void handleMessage(Message msg) { if (!mHasQuit) { if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what); /** Save the current message */ mMsg = msg; /** State that processed the message */ State msgProcessedState = null; if (mIsConstructionCompleted) { /** Normal path */ msgProcessedState = processMsg(msg); } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) { /** Initial one time path. */ mIsConstructionCompleted = true; invokeEnterMethods(0); } else { throw new RuntimeException("StateMachine.handleMessage: " + "The start method not called, received msg: " + msg); } performTransitions(msgProcessedState, msg); // We need to check if mSm == null here as we could be quitting. if (mDbg && mSm != null) mSm.log("handleMessage: X"); } }
/** * Process the message. If the current state doesn't handle * it, call the states parent and so on. If it is never handled then * call the state machines unhandledMessage method. * @return the state that processed the message */ private final State processMsg(Message msg) { StateInfo curStateInfo = mStateStack[mStateStackTopIndex]; if (mDbg) { mSm.log("processMsg: " + curStateInfo.state.getName()); } if (isQuit(msg)) { transitionTo(mQuittingState); } else { while (!curStateInfo.state.processMessage(msg)) { /** * Not processed */ curStateInfo = curStateInfo.parentStateInfo; if (curStateInfo == null) { /** * No parents left so it's not handled */ mSm.unhandledMessage(msg); break; } if (mDbg) { mSm.log("processMsg: " + curStateInfo.state.getName()); } } } return (curStateInfo != null) ? curStateInfo.state : null; }
浠庢暣涓垎鏋愯繃绋嬶紝鍙互寰楀嚭浠ヤ笅鍑犵偣缁撹锛?
- 鍚勪釜鐘舵€佹満鐨別nter()鍜宔xit()鏂规硶鐨勮皟鐢ㄩ『搴忎笌C++缁ф壙浣撶郴涓殑鏋勯€犲嚱鏁颁笌鏋愭瀯鍑芥暟绫讳技銆傚湪鏍戠姸灞傛缁撴瀯涓紝enter()鏂规硶鏄粠鐖剁姸鎬佸埌瀛愮姸鎬佷緷娆¤皟鐢紝鑰宔xit()鏂规硶鍒氬ソ鐩稿弽锛屼粠瀛愮姸鎬佸埌鐖剁姸鎬佷緷娆¤皟鐢ㄣ€?/li>
- 鍦ㄧ姸鎬佸垏鎹㈣繃绋嬩腑锛屽彧浼氭湁鏌愪簺鐘舵€佺殑enter鍜宔xit()鏂规硶琚皟鐢ㄣ€傚湪鏈緥涓紝鍒囨崲鍒癿S4鐘舵€侊紝璋冪敤enter鍜宔xit()鏂规硶鐨勮妭鐐规槸鍒板畠浠渶灏忕殑鍏叡鐖惰妭鐐筸P1涓烘锛屼笖璇ョ埗鑺傜偣涓嶈璋冪敤銆?/li>
- 鍚勪釜鐘舵€佷箣闂达紝濡傛灉鏌愪釜娑堟伅瀛愮姸鎬佷笉鑳藉鐞嗭紝鍏朵細琚緷娆′笂鎶涚粰鍚勪釜鐖剁姸鎬侊紝鐩村埌琚鐞嗕负姝紱鏈€鍚庤繕鏄病琚鐞嗗垯浼氭姏寮冭娑堟伅銆?/li>
更多相关文章
- 代码中设置drawableleft
- android 3.0 隐藏 系统标题栏
- Android开发中activity切换动画的实现
- Android(安卓)学习 笔记_05. 文件下载
- Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
- 技术博客汇总
- android 2.3 wifi (一)
- AndRoid Notification的清空和修改
- Android中的Chronometer