從源代碼角度去分析狀態機的實現過程。
主要涉及到的文件有:
frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java
frameworks/base/core/java/com/android/internal/util/StateMachine.java
frameworks/base/core/java/com/android/internal/util/State.java
frameworks/base/core/java/com/android/internal/util/IState.java
其中IState是一個接口類:
上述的方法會由StateMachine來支配,決定何時調用,現有這個概念即可。
接著看StateMachine類中一些重要的內嵌類和函數。
[cpp]?view plaincopy
????public?static?class?ProcessedMessageInfo?{?? ????????private?int?what;?? ????????private?State?state;?? ????????private?State?orgState;?? ........................................?? ????????ProcessedMessageInfo(Message?message,?State?state,?State?orgState)?{?? ????????????update(message,?state,?orgState);?? ????????}?? ..........................................?? ????????public?void?update(Message?message,?State?state,?State?orgState)?{?? ????????????this.what?=?message.what;?? ????????????this.state?=?state;?? ????????????this.orgState?=?orgState;?? ????????}?? .......................................?? }??
[cpp]?view plaincopy
????private?static?class?ProcessedMessages?{?? ?? ????????private?static?final?int?DEFAULT_SIZE?=?20;?? ?? ????????private?Vector<ProcessedMessageInfo>?mMessages?=?new?Vector<ProcessedMessageInfo>();?? ????????private?int?mMaxSize?=?DEFAULT_SIZE;?? ....................................?? ????????ProcessedMessageInfo?get(int?index)?{?? ????????????int?nextIndex?=?mOldestIndex?+?index;?? ????????????if?(nextIndex?>=?mMaxSize)?{?? ????????????????nextIndex?-=?mMaxSize;?? ????????????}?? ????????????if?(nextIndex?>=?size())?{?? ????????????????return?null;?? ????????????}?else?{?? ????????????????return?mMessages.get(nextIndex);?? ????????????}?? ????????}?? .........................................?? ????????void?add(Message?message,?State?state,?State?orgState)?{?? ????????????mCount?+=?1;?? ????????????if?(mMessages.size()?<?mMaxSize)?{?? ????????????????mMessages.add(new?ProcessedMessageInfo(message,?state,?orgState));?? ????????????}?else?{?? ????????????????ProcessedMessageInfo?pmi?=?mMessages.get(mOldestIndex);?? ????????????????mOldestIndex?+=?1;?? ????????????????if?(mOldestIndex?>=?mMaxSize)?{?? ????????????????????mOldestIndex?=?0;?? ????????????????}?? ????????????????pmi.update(message,?state,?orgState);?? ????????????}?? ????????}?? ????}??
[cpp]?view plaincopy
private?static?class?SmHandler?extends?Handler?{?? .................................?? ????????private?StateInfo?mStateStack[];?? ????????private?int?mStateStackTopIndex?=?-1;?? ????????private?StateInfo?mTempStateStack[];?? ????????private?HaltingState?mHaltingState?=?new?HaltingState();?? ????????private?QuittingState?mQuittingState?=?new?QuittingState();?? ????????private?StateMachine?mSm;?? ????????private?HashMap<State,?StateInfo>?mStateInfo?=?? ????????????new?HashMap<State,?StateInfo>();?? ????????private?State?mInitialState;?? ????????private?State?mDestState;?? ????????@Override?? ????????public?final?void?handleMessage(Message?msg)?{?? ????????????processMsg(msg);?? ????????????performTransitions();?? ?? ????????????if?(mDbg)?Log.d(TAG,?"handleMessage:?X");?? ????????}?? ..........................?? }??
到這里,還是先把wifistatemachine的狀態圖寫出來,以便好理解。
比如,如果當前狀態為mDriverFailedState,則上面mStateStack存放的是[mDefaultState,mDriverUnloadedState,mDriverFailedState]三個狀態的信息
StateMachine的構造函數做了些什么?
[cpp]?view plaincopy
protected?StateMachine(String?name)?{?? ????mSmThread?=?new?HandlerThread(name);?? ????mSmThread.start();?? ????Looper?looper?=?mSmThread.getLooper();?? ?? ????initStateMachine(name,?looper);?? }????
而在initStateMachine方法中:
[cpp]?view plaincopy
private?void?initStateMachine(String?name,?Looper?looper)?{?? ????mName?=?name;?? ????mSmHandler?=?new?SmHandler(looper,?this);??? }??
[cpp]?view plaincopy
private?SmHandler(Looper?looper,?StateMachine?sm)?{?????????????????????????????????????????????????????????????????????????????????????????????????????? ????super(looper);?? ????mSm?=?sm;?? ?? ????addState(mHaltingState,?null);?? ????addState(mQuittingState,?null);?? }??
WifiStateMachine的構造函數主要做了些什么?
主要是調用addState方法將wifi中創建的狀態加入狀態機中,最后setInitialState來設置初始化狀態。具體過程如下:
[cpp]?view plaincopy
protected?final?void?addState(State?state,?State?parent)?{?? ????mSmHandler.addState(state,?parent);?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? }???
[cpp]?view plaincopy
private?final?StateInfo?addState(State?state,?State?parent)?{?? ????if?(mDbg)?{?? ????????Log.d(TAG,?"addStateInternal:?E?state="?+?state.getName()?? ????????????????+?",parent="?+?((parent?==?null)???""?:?parent.getName()));?? ????}?? ????StateInfo?parentStateInfo?=?null;?? ????if?(parent?!=?null)?{?? ????????parentStateInfo?=?mStateInfo.get(parent);?? ????????if?(parentStateInfo?==?null)?{??? ?????????????? ????????????parentStateInfo?=?addState(parent,?null);?? ????????}?? ????}?? ????StateInfo?stateInfo?=?mStateInfo.get(state);?? ????if?(stateInfo?==?null)?{?? ????????stateInfo?=?new?StateInfo();?? ????????mStateInfo.put(state,?stateInfo);?? ????}?? ?? ?????? ????if?((stateInfo.parentStateInfo?!=?null)?&&????? ????????????(stateInfo.parentStateInfo?!=?parentStateInfo))?{?? ????????????throw?new?RuntimeException("state?already?added");?? ????}?? ????stateInfo.state?=?state;?? ????stateInfo.parentStateInfo?=?parentStateInfo;?? ????stateInfo.active?=?false;??? ????if?(mDbg)?Log.d(TAG,?"addStateInternal:?X?stateInfo:?"?+?stateInfo);?? ????return?stateInfo;?? }??
這樣,通過上面的addState方法,最終形成了上圖所示的樹形結構。在使用之前,還要設置state的初始化狀態:
[cpp]?view plaincopy
? ? ? ? ? ?? protected?final?void?setInitialState(State?initialState)?{??????????????????????????????????????????????????????????????????????????????????????????????????? ????mSmHandler.setInitialState(initialState);?? }????
注釋已經說的很清楚,這是第一次接收到消息時候進入的狀態:
[cpp]?view plaincopy
private?final?void?setInitialState(State?initialState)?{????????????????????????????????????????????????????????????????????????????????????????????????? ????if?(mDbg)?Log.d(TAG,?"setInitialState:?initialState"?+?initialState.getName());?? ????mInitialState?=?initialState;?? }??
這樣初始化工作完成,WifiStateMachine的構造函數最后調用start()來啟動狀態機,它的實現方法在父類StateMachine中:
[cpp]?view plaincopy
????public?void?start()?{???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?????????? ????????if?(mSmHandler?==?null)?return;?? ?? ?????????? ????????mSmHandler.completeConstruction();?? ????}?? }??
來看completeConstruction方法的實現:
[cpp]?view plaincopy
private?final?void?completeConstruction()?{?? ????if?(mDbg)?Log.d(TAG,?"completeConstruction:?E");?? ?? ????? ? ? ?? ????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)?Log.d(TAG,?"completeConstruction:?maxDepth="?+?maxDepth);?? ?? ????mStateStack?=?new?StateInfo[maxDepth];?? ????mTempStateStack?=?new?StateInfo[maxDepth];?? ????setupInitialStateStack();?? ?? ????? ? ? ?? ????mIsConstructionCompleted?=?true;?? ????mMsg?=?obtainMessage(SM_INIT_CMD);?? ????invokeEnterMethods(0);?? ?? ????? ? ?? ????performTransitions();?? ?? ????if?(mDbg)?Log.d(TAG,?"completeConstruction:?X");?? }??
接著下一句是setupInitialStateStack方法:
[cpp]?view plaincopy
private?final?void?setupInitialStateStack()?{???????????????????????????????????????????????????????????????????????????????????????????????????????????? ????if?(mDbg)?{?? ????????Log.d(TAG,?"setupInitialStateStack:?E?mInitialState="?? ????????????+?mInitialState.getName());?? ????}?? ?? ????StateInfo?curStateInfo?=?mStateInfo.get(mInitialState);?? ????for?(mTempStateStackCount?=?0;?curStateInfo?!=?null;?mTempStateStackCount++)?{?? ????????mTempStateStack[mTempStateStackCount]?=?curStateInfo;?? ????????curStateInfo?=?curStateInfo.parentStateInfo;?? ????}?? ?? ?????? ????mStateStackTopIndex?=?-1;?? ?? ????moveTempStateStackToStateStack();?? }??
moveTempStateStackToStateStack方法就是將mTempStateStack的值倒序放到mStateStack中:
[cpp]?view plaincopy
private?final?int?moveTempStateStackToStateStack()?{????????????????????????????????????????????????????????????????????????????????????????????????????? ????int?startingIndex?=?mStateStackTopIndex?+?1;?? ????int?i?=?mTempStateStackCount?-?1;?? ????int?j?=?startingIndex;?? ????while?(i?>=?0)?{?? ????????if?(mDbg)?Log.d(TAG,?"moveTempStackToStateStack:?i="?+?i?+?",j="?+?j);?? ????????mStateStack[j]?=?mTempStateStack[i];?? ????????j?+=?1;?? ????????i?-=?1;?? ????}?? ?? ????mStateStackTopIndex?=?j?-?1;?? ????if?(mDbg)?{?? ????????Log.d(TAG,?"moveTempStackToStateStack:?X?mStateStackTop="?? ??????????????+?mStateStackTopIndex?+?",startingIndex="?+?startingIndex?? ??????????????+?",Top="?+?mStateStack[mStateStackTopIndex].state.getName());?? ????}?? ????return?startingIndex;??? }??
這樣,當前狀態信息就保存在mStateStack中,為[mDefaultState, mInitialState]對應的狀態信息。
回到completeConstruction中,接著invokeEnterMethods方法:
[cpp]?view plaincopy
private?final?void?invokeEnterMethods(int?stateStackEnteringIndex)?{????????????????????????????????????????????????????????????????????????????????????? ????for?(int?i?=?stateStackEnteringIndex;?i?<=?mStateStackTopIndex;?i++)?{?? ????????if?(mDbg)?Log.d(TAG,?"invokeEnterMethods:?"?+?mStateStack[i].state.getName());?? ????????mStateStack[i].state.enter();?? ????????mStateStack[i].active?=?true;??? ????}?? }??
mDefaultState重寫enter方法,父類State的enter方法什么也不做;InitialState有重寫enter方法:
[cpp]?view plaincopy
class?InitialState?extends?State?{?? ????@Override?? ?????? ????public?void?enter()?{?? ???????????......................................?? ?? ????????if?(WifiNative.isDriverLoaded())?{?? ????????????transitionTo(mDriverLoadedState);?? ????????}?????? ????????else?{?? ????????????transitionTo(mDriverUnloadedState);?? ????????}?????? ?? ???????????.................................?? ????}?? }??
[cpp]?view plaincopy
protected?final?void?transitionTo(IState?destState)?{?? ????mSmHandler.transitionTo(destState);調用handler方法來處理?? }????
[cpp]?view plaincopy
private?final?void?transitionTo(IState?destState)?{?? ????mDestState?=?(State)?destState;?? ????if?(mDbg)?Log.d(TAG,?"StateMachine.transitionTo?EX?destState"?+?mDestState.getName());??????????????????????????????????????????????????????????????? }??
這樣invokeEnterMethods方法就完成了,接著是performTransitions方法,這是真正處理狀態切換的方法了:
[cpp]?view plaincopy
private?void?performTransitions()?{?? ????.....................?? ????State?destState?=?null;?? ????while?(mDestState?!=?null)?{?? ????????......................................?? ????????destState?=?mDestState;?? ????????mDestState?=?null;?? ????????..............................?? ????????StateInfo?commonStateInfo?=?setupTempStateStackWithStatesToEnter(destState);????????????????????????????????????????????????????????????????????? ????????invokeExitMethods(commonStateInfo);?? ????????int?stateStackEnteringIndex?=?moveTempStateStackToStateStack();?? ????????invokeEnterMethods(stateStackEnteringIndex);?? ?? ?? ????????moveDeferredMessageAtFrontOfQueue();?? ????}????? ????if?(destState?!=?null)?{?? ????????if?(destState?==?mQuittingState)?{?? ????????????cleanupAfterQuitting();?? ?? ????????}?else?if?(destState?==?mHaltingState)?{?? ?? ????????????mSm.halting();?? ????????}?? ????}?? }??
setupTempStateStackWithStatesToEnter方法主要是把當前活動state家族放入mTempStateStack中,并且返回頂層節點的信息,這樣,mTempStateStack保存信息為[mDriverUnLoadedState,?mDefaultState]的信息;
invokeExitMethods方法則是從當前活動state頂層節點一次調用exit方法,并且設置state的active標記為false;這里要注意當前活動的還是是mStateStack中的信息,即[mDefaultState, mInitialState]對應的狀態信息,exit方法一般是做一些善后工作。處理完成后
mStateStackTopIndex又自減為-1。
moveTempStateStackToStateStack和invokeEnterMethods方法前面已經分析過。完成后mStateStack的狀態信息變為[mDefaultState,mDriverUnLoadedState]
最后一個函數moveDeferredMessageAtFrontOfQueue刷新消息隊列的排序。該方法就是將mDeferredMessages容器中的消息按先后順序發送出去,然后清空容器。至于消息是如何加入容器的,后面遇到再分析。
這樣start方法就完成,整個WifiStateMachine構造函數也建立完成了。
如果我們打開wifi,會在WifiService中調用setWifiEnabled - >mWifiStateMachine.setWifiEnabled來啟動。setWifiEnabled的狀態切換過程為:
[cpp]?view plaincopy
public?void?setWifiEnabled(boolean?enable)?{????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ????mLastEnableUid.set(Binder.getCallingUid());?? ????if?(enable)?{?? ?????????? ????????sendMessage(obtainMessage(CMD_LOAD_DRIVER,?WIFI_STATE_ENABLING,?0));?? ????????sendMessage(CMD_START_SUPPLICANT);?? ????}?else?{?? ????????sendMessage(CMD_STOP_SUPPLICANT);?? ?????????? ????????sendMessage(obtainMessage(CMD_UNLOAD_DRIVER,?WIFI_STATE_DISABLED,?0));?? ????}?? }??
sendMessage方法是StateMachine實現的,它封裝了Handler類的sendMessage方法,會被SmHandler類的handleMessage來接收處理。而它是調用一下兩個方法來處理消息的:
[cpp]?view plaincopy
processMsg(msg);?? performTransitions();??
先看第一個:
[cpp]?view plaincopy
private?final?void?processMsg(Message?msg)?{?? ????StateInfo?curStateInfo?=?mStateStack[mStateStackTopIndex];?? ????if?(mDbg)?{?? ????????Log.d(TAG,?"processMsg:?"?+?curStateInfo.state.getName());?? ????}?? ????while?(!curStateInfo.state.processMessage(msg))?{?? ????????? ? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ????????curStateInfo?=?curStateInfo.parentStateInfo;?? ????????if?(curStateInfo?==?null)?{?? ????????????? ? ?? ????????????mSm.unhandledMessage(msg);?? ????????????if?(isQuit(msg))?{?? ????????????????transitionTo(mQuittingState);?? ????????????}?? ????????????break;?? ????????}?? ????????if?(mDbg)?{?? ????????????Log.d(TAG,?"processMsg:?"?+?curStateInfo.state.getName());?? ????????}?? ????}?? ?? ????? ? ?? ????if?(curStateInfo?!=?null)?{?? ????????State?orgState?=?mStateStack[mStateStackTopIndex].state;?? ????????mProcessedMessages.add(msg,?curStateInfo.state,?orgState);?? ????}?else?{?? ????????mProcessedMessages.add(msg,?null,?null);?? ????}?? }??
這時mStateStack的狀態信息變為[mDefaultState, mDriverUnLoadedState],先所以調用mDriverUnLoadedState的processMessage方法,而前面發送的消息為:
[cpp]?view plaincopy
sendMessage(obtainMessage(CMD_LOAD_DRIVER,?WIFI_STATE_ENABLING,?0));?? sendMessage(CMD_START_SUPPLICANT);??
第一句的CMD_LOAD_DRIVER,發送給應用層的消息為WIFI_STATE_ENABLING:
[cpp]?view plaincopy
class?DriverUnloadedState?extends?State?{???????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ????@Override?? ????public?void?enter()?{?? ????????if?(DBG)?log(getName()?+?"\n");?? ????????EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED,?getName());?? ????}?? ????@Override?? ????public?boolean?processMessage(Message?message)?{?? ????????if?(DBG)?log(getName()?+?message.toString()?+?"\n");?? ????????switch?(message.what)?{?? ????????????case?CMD_LOAD_DRIVER:?? ????????????????mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING);?? ????????????????transitionTo(mWaitForP2pDisableState);?? ????????????????break;?? ????????????case?WifiP2pService.P2P_ENABLE_PENDING:?? ????????????????mReplyChannel.replyToMessage(message,?P2P_ENABLE_PROCEED);?? ????????????????break;?? ????????????default:?? ????????????????return?NOT_HANDLED;?? ????????}?? ????????EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED,?message.what);?? ????????return?HANDLED;?? ????}?? }??
WaitForP2pDisableState的enter做什么了?
狀態已經進入mWaitForP2pDisableState中了:
[cpp]?view plaincopy
class?WaitForP2pDisableState?extends?State?{?? ????.............................?? ????@Override?? ????public?boolean?processMessage(Message?message)?{?? ????????????case?WifiP2pService.WIFI_ENABLE_PROCEED:??? ?????????????????? ????????????????message.arg1?=?mSavedArg;?? ????????????????transitionTo(mDriverLoadingState);?? ????????????????break;?? ????????????case?CMD_LOAD_DRIVER:?? ????????????case?CMD_UNLOAD_DRIVER:?? ????????????case?CMD_START_SUPPLICANT:?? ???????????..........................?? ????????????????deferMessage(message);?? ???????????.............................??
deferMessage將消息放入mDeferredMessages容器中,前面有提及過,作用是要把消息保存起來,等切換到下一個狀態后,再將消息發送出去,讓下一個狀態接收;接下來的performTransitions方法中會將消息發送出去,并且在切換下一個狀態前清空容器。
這時候狀態在WaitForP2pDisableState中,WifiP2pService接收到消息WIFI_ENABLE_PENDING后,返回WIFI_ENABLE_PROCEED作為響應;
所以執行transitionTo(mDriverLoadingState)進入下一個狀態。
processMsg就處理完成了,接著的performTransitions前面已經分析過,主要工作為調用當前狀態的exit方法;切換新狀態到mSupplicantStartingState,并調用新狀態的enter方法,靜等下一個消息的到來。
根據前面的分析可知,這時候先執行的是mDriverLoadingState的enter方法:
[cpp]?view plaincopy
class?DriverLoadingState?extends?State?{?? ????@Override?? ????public?void?enter()?{?? ????????if?(DBG)?log(getName()?+?"\n");?? ????????EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED,?getName());?? ?? ????????final?Message?message?=?new?Message();?? ????????message.copyFrom(getCurrentMessage());?? ????????? ? ?? ????????new?Thread(new?Runnable()?{?? ????????????public?void?run()?{?? ????????????????mWakeLock.acquire();?? ?????????????????? ????????????????switch(message.arg1)?{?? ????????????????????case?WIFI_STATE_ENABLING:?? ????????????????????????setWifiState(WIFI_STATE_ENABLING);?? ????????????????????????break;?? ????????????????if(WifiNative.loadDriver())?{?? ????????????????????if?(DBG)?log("Driver?load?successful");?? ????????????????????sendMessage(CMD_LOAD_DRIVER_SUCCESS);?? ????????????????}?else?{?? ????????????????????loge("Failed?to?load?driver!");?????????????????????????????????????????????????????????????????????????????????????????????????????????? ????????????????????switch(message.arg1)?{?? ????????????????????????case?WIFI_STATE_ENABLING:?? ????????????????????????????setWifiState(WIFI_STATE_UNKNOWN);?? ????????????????????????????break;?? ????????????????????????case?WIFI_AP_STATE_ENABLING:?? ????????????????????????????setWifiApState(WIFI_AP_STATE_FAILED);?? ????????????????????????????break;?? ????????????????????}?? ????????????????????sendMessage(CMD_LOAD_DRIVER_FAILURE);?? ????????????????}?? ????????????????mWakeLock.release();?? ????????????}?? ????????}).start();?? ?? ????@Override?? ????public?boolean?processMessage(Message?message)?{?? ????????if?(DBG)?log(getName()?+?message.toString()?+?"\n");?? ????????switch?(message.what)?{?? ????????????case?CMD_LOAD_DRIVER_SUCCESS:?? ????????????????transitionTo(mDriverLoadedState);?? ????????????????break;?? ????????????case?CMD_LOAD_DRIVER_FAILURE:?? ????????????????transitionTo(mDriverFailedState);?? ????????????????break;?? ????????????case?CMD_LOAD_DRIVER:?? ????????????case?CMD_UNLOAD_DRIVER:?? ????????????case?CMD_START_SUPPLICANT:?? ????????????case?CMD_STOP_SUPPLICANT:?? ????????????case?CMD_START_AP:?? ????????????case?CMD_STOP_AP:?? ????????????case?CMD_START_DRIVER:?? ????????????case?CMD_STOP_DRIVER:?? ????????????case?CMD_SET_SCAN_MODE:?? ????????????case?CMD_SET_SCAN_TYPE:?? ????????????case?CMD_SET_HIGH_PERF_MODE:?? ????????????case?CMD_SET_COUNTRY_CODE:?? ????????????case?CMD_SET_FREQUENCY_BAND:?? ????????????case?CMD_START_PACKET_FILTERING:?? ????????????case?CMD_STOP_PACKET_FILTERING:?? ????????????????deferMessage(message);?? ????????????????break;?? ????????????default:?? ????????????????return?NOT_HANDLED;?? ????????}?? ????????EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED,?message.what);?? ????????return?HANDLED;?? ????}??
enter線程中的消息為前面發送的WIFI_STATE_ENABLING,setWifiState的作用是發送廣播通知應用層wifi狀態的改變,并且加載JNI的wifi驅動,成功后還發送CMD_LOAD_DRIVER_SUCCESS消息。
接著第二句命令CMD_START_SUPPLICANT,狀態在mDriverLoadingState中,執行processMessage方法,這時候會先接收前面的延時消息CMD_LOAD_DRIVER_SUCCESS,接著接收CMD_LOAD_DRIVER,CMD_START_SUPPLICANT
狀態就切換到了mDriverLoadedState,并且將其他兩條消息延后再傳給下一個狀態。
后續WifiService會根據邏輯需求,發送各種命令過來進行狀態的切換,但流程都和上述分析的一樣,狀態機能確保各種狀態有條不紊的切換并保持控制流程的清晰明了。
最后畫一幅流程圖如下:
總結
以上是生活随笔為你收集整理的android状态机实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。