編輯:關於Android編程
先說兩句題外話,很感謝android,在這裡能看到很多優秀的代碼。同時也感覺到外面的工程師真的很厲害,都是java人家就能寫出這麼牛的東西。感慨之下就有了些思考:我們絕大多數人只要把那些牛人已經創造出來的牛逼的東西,記住並且弄懂就是一件非常不錯的事情,至少能衣食無憂。:-D 讀書的時候需要經常做題,在理解的基礎上記住解題方法基本就能很牛了,事實上高考中考絕大多數都是已經有過的題型,能做到前面所說的應該能進入不錯的學校。工作後,慢慢也發現很多了不起的技術,都是在國外已經發展的很成熟基礎上學習過來的。作為一個普通人,還是不要天天談創新的好,hold不住,把基礎的東西記住掌握即可。說了一堆,也算聊以自慰。
我們知道類的成員可以分為兩種:方法和屬性。大多數情況下,對於一個狀態,比如某數大於0,類的方法都只能做出一種對應的操作,並且類的本身並不考慮外部狀態。android的狀態機就屬於大多數之後的那一小部分。對於某個事件,或者更准確的說,某一個消息,在不同的狀態下能做出不同的操作。並且android狀態機中的狀態是繼承的,就像數據結構中的樹一樣,如果當前節點(狀態)不能對這個事件做出響應,就會到父節點繼續判斷並且做出響應,在下面的講述中,我們稱這個為狀態路徑,而對於所有狀態稱為狀態樹。這一句話已經從整體上對狀態機進行了概括,記住這些對後面的理解很有好處。
State,狀態機中的狀態封裝類,這個類主要是實現了IState接口。其中有狀態的基本方法,enter,exit以及消息處理方法processMessage。enter方法在狀態機轉入這個狀態中會進行調用,exit方法在狀態機轉出這個方法時候會調用。這裡對於一個很簡單的類,google使用了接口屬性,說說自己的理解。接口中的方法都是公有方法,並且只能聲明常量。將主要方法都放在接口中聲明,一方面限制了方法的定義,一方面也突出了這個類主要就是擁有某種功能。另外在State裡面,聲明了一個protect類型的構造方法,這樣其他類就不可以直接聲明state類的對象。state在狀態機statemachine類裡面是以StateInfo類型使用的,這個基本不影響訪問。
statemachine裡面主要工作都是由SmHandler類來完成的,statemachine本身絕大多數方法都是都是對SmHandler方法的再次封裝。另外為了能夠做到及時響應主線程的消息,又聲明了一個HandlerThread,主要任務都是在這個線程裡面實現的。
現在直接去看SmHandler類吧,其最主要的方法就是handleMessage。該方法的主要是三大模塊,第一個消息處理,或者說是分配到對應的狀態再有對應的狀態進行處理比較合適,第二個狀態的初始化,大概可以理解成執行初始化狀態路徑上每個狀態的enter方法,第三個執行狀態轉移,即更新狀態樹。
[java]
if (mIsConstructionCompleted) {
/** Normal path */
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();//第三個執行狀態轉移
if (mIsConstructionCompleted) {
/** Normal path */
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();//第三個執行狀態轉移首先去看下processMsg方法
[java]
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) {
Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
}
}
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) {
Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
}
}從這段代碼中(!curStateInfo.state.processMessage(msg))就說明了:如果當前狀態執行完processMessage方法返回了false,也就是對當前消息NOT_HANDLED,那麼就會持續調用這個狀態的父狀態執行方法。一般終有一個狀態能夠處理消息的,如果真的沒有處理,會記錄到unhandledMessage方法裡面的。
接下來先看下狀態轉移performTransitions方法,首先碰到mDestState,這是SmHandler裡面的一個標記,指向當前狀態路徑最下面的一個狀態,後面都是父狀態。可以在其他時候調用private final void transitionTo(IState destState)方法(外面的接口方法)改變當前mDestState的指向。這樣處理完當前消息之後,performTransitions就會根據最新的mDestState來進行狀態路徑切換。狀態切換有點像樹的遍歷一樣,並不是回到根節點在進行搜索到新的節點,而是會回到原來的mDestState和最新的mDestState最近的一個共同祖先,然後在走向新的mDestState狀態。進行狀態切換主要是執行原狀態路徑上需要退出的狀態的exit()方法,和新的狀態路徑上的enter()方法。
最後看下狀態機的初始化invokeEnterMethods,這個直接看狀態機的實例比較方便。看一個簡單一些的應用,藍牙APK裡面關於耳機和電話連接處理的HeadsetStateMachine類,其構造方法中,關於狀態機的代碼如下:
[java]
addState(mDisconnected);
addState(mPending);
addState(mConnected);
addState(mAudioOn);
setInitialState(mDisconnected);
addState(mDisconnected);
addState(mPending);
addState(mConnected);
addState(mAudioOn);
setInitialState(mDisconnected);以上兩塊代碼,第一塊是建立本狀態機的整個框架,就相當於建立整個狀態樹,第二個是設置初始化狀態。再看看HeadsetStateMachine中的靜態代碼塊
[java]
static HeadsetStateMachine make(HeadsetService context) {
Log.d(TAG, "make");
HeadsetStateMachine hssm = new HeadsetStateMachine(context);
hssm.start();
return hssm;
}
static HeadsetStateMachine make(HeadsetService context) {
Log.d(TAG, "make");
HeadsetStateMachine hssm = new HeadsetStateMachine(context);
hssm.start();
return hssm;
}以上兩塊代碼,第一塊是建立本狀態機的整個框架,就相當於建立整個狀態樹,第二個是設置初始化狀態。再看看HeadsetStateMachine中的靜態代碼塊
這裡面有一個start()方法,從這個方法開始,狀態機開始運作,包括分配內存,根據初始化狀態設置初始化狀態路徑,這一點主要在setupInitialStateStack方法中執行,依次執行狀態路徑上每個狀態的enter方法,這個使用了消息機制sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));,最終就在本段開頭的invokeEnterMethods方法中執行。
到這裡狀態機主要內容基本講解完畢,貌似絕大多數都需要記憶,記住了感覺就理解到了。:-D 有點像本文開頭說的。初一看感覺沒有什麼,但是如果想象下你有一個這樣的需求,耳機和手機的狀態一直在切換,你會采用什麼方式去做,在考慮了很多之後會感覺狀態機真的是一個很厲害的東西。:-D 接下來附上android源碼中的demo,為了方便理解,筆者將輸出增加了一些空行,多余的空行不是demo打印的。
[java]
class Hsm1 extends StateMachine {
private static final String TAG = "hsm1";
public static final int CMD_1 = 1;
public static final int CMD_2 = 2;
public static final int CMD_3 = 3;
public static final int CMD_4 = 4;
public static final int CMD_5 = 5;
public static Hsm1 makeHsm1() {
Log.d(TAG, "makeHsm1 E");
Hsm1 sm = new Hsm1("hsm1");
sm.start();
Log.d(TAG, "makeHsm1 X");
return sm;
}
Hsm1(String name) {
super(name);
Log.d(TAG, "ctor E");
// Add states, use indentation to show hierarchy
addState(mP1);
addState(mS1, mP1);
addState(mS2, mP1);
addState(mP2);
// Set the initial state
setInitialState(mS1);
Log.d(TAG, "ctor X");
}
class P1 extends State {
@Override public void enter() {
Log.d(TAG, "mP1.enter");
}
@Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mP1.processMessage what=" + message.what);
switch(message.what) {
case CMD_2:
// CMD_2 will arrive in mS2 before CMD_3
sendMessage(obtainMessage(CMD_3));
deferMessage(message);
transitionTo(mS2);
retVal = HANDLED;
break;
default:
// Any message we don't understand in this state invokes unhandledMessage
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override public void exit() {
Log.d(TAG, "mP1.exit");
}
}
class S1 extends State {
@Override public void enter() {
Log.d(TAG, "mS1.enter");
}
@Override public boolean processMessage(Message message) {
Log.d(TAG, "S1.processMessage what=" + message.what);
if (message.what == CMD_1) {
// Transition to ourself to show that enter/exit is called
transitionTo(mS1);
return HANDLED;
} else {
// Let parent process all other messages
return NOT_HANDLED;
}
}
@Override public void exit() {
Log.d(TAG, "mS1.exit");
}
}
class S2 extends State {
@Override public void enter() {
Log.d(TAG, "mS2.enter");
}
@Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mS2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_2):
sendMessage(obtainMessage(CMD_4));
retVal = HANDLED;
break;
case(CMD_3):
deferMessage(message);
transitionTo(mP2);
retVal = HANDLED;
break;
default:
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override public void exit() {
Log.d(TAG, "mS2.exit");
}
}
class P2 extends State {
@Override public void enter() {
Log.d(TAG, "mP2.enter");
sendMessage(obtainMessage(CMD_5));
}
@Override public boolean processMessage(Message message) {
Log.d(TAG, "P2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_3):
break;
case(CMD_4):
break;
case(CMD_5):
transitionToHaltingState();
break;
}
return HANDLED;
}
@Override public void exit() {
Log.d(TAG, "mP2.exit");
}
}
@Override
void onHalting() {
Log.d(TAG, "halting");
synchronized (this) {
this.notifyAll();
}
}
P1 mP1 = new P1();
S1 mS1 = new S1();
S2 mS2 = new S2();
P2 mP2 = new P2();
}
</code>
* <p>If this is executed by sending two messages CMD_1 and CMD_2
* (Note the synchronize is only needed because we use hsm.wait())</p>
<code>
Hsm1 hsm = makeHsm1();
synchronize(hsm) {
hsm.sendMessage(obtainMessage(hsm.CMD_1));
hsm.sendMessage(obtainMessage(hsm.CMD_2));
try {
// wait for the messages to be handled
hsm.wait();
} catch (InterruptedException e) {
Log.e(TAG, "exception while waiting " + e.getMessage());
}
}
輸出如下:
D/hsm1 ( 1999): makeHsm1 E
D/hsm1 ( 1999): ctor E
D/hsm1 ( 1999): ctor X
D/hsm1 ( 1999): mP1.enter
D/hsm1 ( 1999): mS1.enter
D/hsm1 ( 1999): makeHsm1 X
D/hsm1 ( 1999): mS1.processMessage what=1
D/hsm1 ( 1999): mS1.exit
D/hsm1 ( 1999): mS1.enter
D/hsm1 ( 1999): mS1.processMessage what=2
D/hsm1 ( 1999): mP1.processMessage what=2
D/hsm1 ( 1999): mS1.exit
D/hsm1 ( 1999): mS2.enter
D/hsm1 ( 1999): mS2.processMessage what=2
D/hsm1 ( 1999): mS2.processMessage what=3
D/hsm1 ( 1999): mS2.exit
D/hsm1 ( 1999): mP1.exit
D/hsm1 ( 1999): mP2.enter
D/hsm1 ( 1999): mP2.processMessage what=3
D/hsm1 ( 1999): mP2.processMessage what=4
D/hsm1 ( 1999): mP2.processMessage what=5
D/hsm1 ( 1999): mP2.exit
D/hsm1 ( 1999): halting
class Hsm1 extends StateMachine {
private static final String TAG = "hsm1";
public static final int CMD_1 = 1;
public static final int CMD_2 = 2;
public static final int CMD_3 = 3;
public static final int CMD_4 = 4;
public static final int CMD_5 = 5;
public static Hsm1 makeHsm1() {
Log.d(TAG, "makeHsm1 E");
Hsm1 sm = new Hsm1("hsm1");
sm.start();
Log.d(TAG, "makeHsm1 X");
return sm;
}
Hsm1(String name) {
super(name);
Log.d(TAG, "ctor E");
// Add states, use indentation to show hierarchy
addState(mP1);
addState(mS1, mP1);
addState(mS2, mP1);
addState(mP2);
// Set the initial state
setInitialState(mS1);
Log.d(TAG, "ctor X");
}
class P1 extends State {
@Override public void enter() {
Log.d(TAG, "mP1.enter");
}
@Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mP1.processMessage what=" + message.what);
switch(message.what) {
case CMD_2:
// CMD_2 will arrive in mS2 before CMD_3
sendMessage(obtainMessage(CMD_3));
deferMessage(message);
transitionTo(mS2);
retVal = HANDLED;
break;
default:
// Any message we don't understand in this state invokes unhandledMessage
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override public void exit() {
Log.d(TAG, "mP1.exit");
}
}
class S1 extends State {
@Override public void enter() {
Log.d(TAG, "mS1.enter");
}
@Override public boolean processMessage(Message message) {
Log.d(TAG, "S1.processMessage what=" + message.what);
if (message.what == CMD_1) {
// Transition to ourself to show that enter/exit is called
transitionTo(mS1);
return HANDLED;
} else {
// Let parent process all other messages
return NOT_HANDLED;
}
}
@Override public void exit() {
Log.d(TAG, "mS1.exit");
}
}
class S2 extends State {
@Override public void enter() {
Log.d(TAG, "mS2.enter");
}
@Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mS2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_2):
sendMessage(obtainMessage(CMD_4));
retVal = HANDLED;
break;
case(CMD_3):
deferMessage(message);
transitionTo(mP2);
retVal = HANDLED;
break;
default:
retVal = NOT_HANDLED;
break;
}
return retVal;
}
@Override public void exit() {
Log.d(TAG, "mS2.exit");
}
}
class P2 extends State {
@Override public void enter() {
Log.d(TAG, "mP2.enter");
sendMessage(obtainMessage(CMD_5));
}
@Override public boolean processMessage(Message message) {
Log.d(TAG, "P2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_3):
break;
case(CMD_4):
break;
case(CMD_5):
transitionToHaltingState();
break;
}
return HANDLED;
}
@Override public void exit() {
Log.d(TAG, "mP2.exit");
}
}
@Override
void onHalting() {
Log.d(TAG, "halting");
synchronized (this) {
this.notifyAll();
}
}
P1 mP1 = new P1();
S1 mS1 = new S1();
S2 mS2 = new S2();
P2 mP2 = new P2();
}
</code>
* <p>If this is executed by sending two messages CMD_1 and CMD_2
* (Note the synchronize is only needed because we use hsm.wait())</p>
<code>
Hsm1 hsm = makeHsm1();
synchronize(hsm) {
hsm.sendMessage(obtainMessage(hsm.CMD_1));
hsm.sendMessage(obtainMessage(hsm.CMD_2));
try {
// wait for the messages to be handled
hsm.wait();
} catch (InterruptedException e) {
Log.e(TAG, "exception while waiting " + e.getMessage());
}
}
輸出如下:
D/hsm1 ( 1999): makeHsm1 E
D/hsm1 ( 1999): ctor E
D/hsm1 ( 1999): ctor X
D/hsm1 ( 1999): mP1.enter
D/hsm1 ( 1999): mS1.enter
D/hsm1 ( 1999): makeHsm1 X
D/hsm1 ( 1999): mS1.processMessage what=1
D/hsm1 ( 1999): mS1.exit
D/hsm1 ( 1999): mS1.enter
D/hsm1 ( 1999): mS1.processMessage what=2
D/hsm1 ( 1999): mP1.processMessage what=2
D/hsm1 ( 1999): mS1.exit
D/hsm1 ( 1999): mS2.enter
D/hsm1 ( 1999): mS2.processMessage what=2
D/hsm1 ( 1999): mS2.processMessage what=3
D/hsm1 ( 1999): mS2.exit
D/hsm1 ( 1999): mP1.exit
D/hsm1 ( 1999): mP2.enter
D/hsm1 ( 1999): mP2.processMessage what=3
D/hsm1 ( 1999): mP2.processMessage what=4
D/hsm1 ( 1999): mP2.processMessage what=5
D/hsm1 ( 1999): mP2.exit
D/hsm1 ( 1999): halting
什麼是wipe?wipe什麼意思?雙wipe又是什麼?安卓手機如何wipe?對於剛接觸安卓手機的同學來說是比較模糊的概念,那到底wipe是什麼意思呢,下面來
Android開發中,我們可以使用Android開發軟件如:Eclipse、Android Studio自帶的模擬器或者genymotion,進行測試,可是我們在調試中,
最近有一段時間沒寫博客了,一方面是工作比較忙,一方面也著實本人水平有限,沒有太多能與大家分享的東西,也就是在最近公司要做一個搶紅包的功能,老板發話了咋們就開干呗,本人就開
Cocos2d-x 3.0 - Eclipse上構建一個Android項目2014年4月30日 4月末 本篇繼續介紹Cocos2d-x 3.0的一些基礎內容,前面一篇博客