Android中状态机

State,状态机中的状态封装类,这个类主要是实现了IState接口。其中有状态的基本方法,enter,exit以及消息处理方法processMessage。enter方法在状态机转入这个状态中会进行调用,exit方法在状态机转出这个方法时候会调用。这里对于一个很简单的类,google使用了接口属性,说说自己的理解。接口中的方法都是公有方法,并且只能声明常量。将主要方法都放在接口中声明,一方面限制了方法的定义,一方面也突出了这个类主要就是拥有某种功能。另外在State里面,声明了一个protect类型的构造方法,这样其他类就不可以直接声明state类的对象。state在状态机statemachine类里面是以StateInfo类型使用的,这个基本不影响访问。
statemachine里面主要工作都是由SmHandler类来完成的,statemachine本身绝大多数方法都是都是对SmHandler方法的再次封装。另外为了能够做到及时响应主线程的消息,又声明了一个HandlerThread,主要任务都是在这个线程里面实现的。
        现在直接去看SmHandler类吧,其最主要的方法就是handleMessage。该方法的主要是三大模块,第一个消息处理,或者说是分配到对应的状态再有对应的状态进行处理比较合适,第二个状态的初始化,大概可以理解成执行初始化状态路径上每个状态的enter方法,第三个执行状态转移,即更新状态树。

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方法

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类,其构造方法中,关于状态机的代码如下:

addState(mDisconnected);  
addState(mPending);  
addState(mConnected);  
addState(mAudioOn);  
  
setInitialState(mDisconnected);  

以上两块代码,第一块是建立本状态机的整个框架,就相当于建立整个状态树,第二个是设置初始化状态。再看看HeadsetStateMachine中的静态代码块

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方法中执行。

 

源码frameworks/base/core/java/com/android/internal/util/StateMachine.java

源码中测试case:

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  

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值