Android系统自带的层次状态机StateMachine(Hierarchical State Machine)

Android在framework层自己实现一套层次状态机,总共有三个类:StateMachine,State和IState,这三个类直接从Android平台代码中没有公开出来提供给开发者使用,但是可以直接把源代码复制出来使用,见附录源代码。

下一个Android平台自带的层次状态机(HSM)简单示例。每一个状态不做过多处理,是当前状态所属消息,简单的打印字符串并返回true(StateMachine.HANDLED)。否则返回false(StateMachine.NOT_HANDLED)。

状态定义:

public interface StateDef {//状态标志常量。int CMD_WAKE_UP = 1;int CMD_HUNGRY = 2;int CMD_WORK = 3;int CMD_TIRED = 4;int CMD_EAT = 5;int CMD_GETUP = 6;int CMD_OFFWORK = 7;int CMD_SLEEP = 8;
}
import android.os.Message;//起床状态。
public class GetupState extends State {private HumanStateMachine mStateMachine;public GetupState(StateMachine sm) {this.mStateMachine = (HumanStateMachine) sm;}@Overridepublic boolean processMessage(Message msg) {System.out.println("GetupState:" + msg.what);boolean ret;switch (msg.what) {case StateDef.CMD_GETUP:System.out.println("GetupState:起床...");ret = StateMachine.HANDLED;break;default:ret = StateMachine.NOT_HANDLED;break;}return ret;}@Overridepublic void enter() {System.out.println(getName() + "enter");}@Overridepublic void exit() {System.out.println(getName() + "exit");}@Overridepublic String getName() {return "起床";}
}
import android.os.Message;//缺省的状态处理,转发。
public class DefaultState extends State {private HumanStateMachine mStateMachine;public DefaultState(StateMachine sm) {this.mStateMachine = (HumanStateMachine) sm;}@Overridepublic boolean processMessage(Message msg) {System.out.println("DefaultState:" + msg.what);boolean ret;switch (msg.what) {case StateDef.CMD_WAKE_UP:System.out.println("DefaultState->切换起床状态");mStateMachine.deferMessage(mStateMachine.obtainMessage(StateDef.CMD_GETUP));mStateMachine.transitionTo(mStateMachine.mGetupState);ret = StateMachine.HANDLED;break;case StateDef.CMD_GETUP:System.out.println("DefaultState->切换起床状态");mStateMachine.deferMessage(mStateMachine.obtainMessage(StateDef.CMD_GETUP));mStateMachine.transitionTo(mStateMachine.mGetupState);ret = StateMachine.HANDLED;break;case StateDef.CMD_WORK:System.out.println("DefaultState->切换工作状态");mStateMachine.deferMessage(mStateMachine.obtainMessage(StateDef.CMD_WORK));mStateMachine.transitionTo(mStateMachine.mWorkState);ret = StateMachine.HANDLED;break;case StateDef.CMD_HUNGRY:System.out.println("DefaultState->切换吃饭状态");mStateMachine.deferMessage(mStateMachine.obtainMessage(StateDef.CMD_EAT));mStateMachine.transitionTo(mStateMachine.mEatState);ret = StateMachine.HANDLED;break;case StateDef.CMD_TIRED:System.out.println("DefaultState->切换睡觉状态");mStateMachine.deferMessage(mStateMachine.obtainMessage(StateDef.CMD_SLEEP));mStateMachine.transitionTo(mStateMachine.mSleepState);ret = StateMachine.HANDLED;break;default:System.out.println("缺省未处理");ret = StateMachine.HANDLED;break;}return ret;}@Overridepublic void enter() {System.out.println(getName() + "enter");}@Overridepublic void exit() {System.out.println(getName() + "exit");}@Overridepublic String getName() {return "缺省";}
}
//状态机。
public class HumanStateMachine extends StateMachine {DefaultState mDefaultState = new DefaultState(this);EatState mEatState = new EatState(this);SleepState mSleepState = new SleepState(this);WorkState mWorkState = new WorkState(this);GetupState mGetupState = new GetupState(this);OffworkState mOffworkState = new OffworkState(this);protected HumanStateMachine(String name) {super(name);init();}private void init() {//初始化状态,层次关系,树形结构。addState(mGetupState, mDefaultState);addState(mWorkState, mGetupState);addState(mEatState, mGetupState);addState(mOffworkState, mDefaultState);addState(mSleepState, mDefaultState);setInitialState(mSleepState);start(); // 状态机初始化完成,等待外界命令。}
}
import android.os.Message;//工作状态。
public class WorkState extends State {private HumanStateMachine mStateMachine;public WorkState(StateMachine sm) {mStateMachine = (HumanStateMachine) sm;}@Overridepublic void enter() {System.out.println(getName() + "enter");}@Overridepublic void exit() {System.out.println(getName() + "exit");}@Overridepublic String getName() {return "工作";}@Overridepublic boolean processMessage(Message msg) {System.out.println("WorkState:" + msg.what);boolean ret;switch (msg.what) {case StateDef.CMD_WORK:System.out.println("WorkState:工作...");ret = StateMachine.HANDLED;break;default:ret = StateMachine.NOT_HANDLED;break;}return ret;}
}
import android.os.Message;
//吃饭状态。
public class EatState extends State {private HumanStateMachine mStateMachine;public EatState(StateMachine sm) {this.mStateMachine = (HumanStateMachine) sm;}@Overridepublic void enter() {System.out.println(getName() + "enter");}@Overridepublic void exit() {System.out.println(getName() + "exit");}@Overridepublic boolean processMessage(Message msg) {System.out.println("EatState:" + msg.what);boolean ret;switch (msg.what) {case StateDef.CMD_EAT:System.out.println("EatState:吃...");ret = StateMachine.HANDLED;break;default:ret = StateMachine.NOT_HANDLED;break;}return ret;}@Overridepublic String getName() {return "吃";}
}
import android.os.Message;//下班状态。
public class OffworkState extends State {private HumanStateMachine mStateMachine;public OffworkState(StateMachine sm) {mStateMachine = (HumanStateMachine) sm;}@Overridepublic void enter() {System.out.println(getName() + "enter");;}@Overridepublic void exit() {System.out.println(getName() + "exit");}@Overridepublic boolean processMessage(Message msg) {System.out.println("OffworkState:" + msg.what);boolean ret;switch (msg.what) {case StateDef.CMD_OFFWORK:System.out.println("OffworkState...");ret = StateMachine.HANDLED;break;default:ret = StateMachine.NOT_HANDLED;break;}return ret;}@Overridepublic String getName() {return "下班";}
}
import android.os.Message;//睡觉状态。
public class SleepState extends State {private HumanStateMachine mStateMachine;public SleepState(StateMachine sm) {this.mStateMachine = (HumanStateMachine) sm;}@Overridepublic void enter() {System.out.println(getName() + "enter");}@Overridepublic void exit() {System.out.println(getName() + "exit");}@Overridepublic String getName() {return "睡觉";}@Overridepublic boolean processMessage(Message msg) {System.out.println("SleepState:" + msg.what);boolean ret;switch (msg.what) {case StateDef.CMD_SLEEP:System.out.println("SleepState:睡觉...");ret = StateMachine.HANDLED;break;default:ret = StateMachine.NOT_HANDLED;break;}return ret;}
}

测试:

    private void test() {HumanStateMachine sm = new HumanStateMachine("人");sm.sendMessage(sm.obtainMessage(StateDef.CMD_TIRED));sm.sendMessage(sm.obtainMessage(StateDef.CMD_HUNGRY));sm.quit();}

输出:

I/System.out: 缺省enter
I/System.out: 睡觉enter
I/System.out: SleepState:4
I/System.out: DefaultState:4
I/System.out: DefaultState->切换睡觉状态
I/System.out: 睡觉exit
I/System.out: 睡觉enter
I/System.out: SleepState:8
I/System.out: SleepState:睡觉...
I/System.out: SleepState:2
I/System.out: DefaultState:2
I/System.out: DefaultState->切换吃饭状态
I/System.out: 睡觉exit
I/System.out: 起床enter
I/System.out: 吃enter
I/System.out: EatState:5
I/System.out: EatState:吃...
I/System.out: 吃exit
I/System.out: 起床exit
I/System.out: 缺省exit

附:

经典状态机模式:

https://blog.csdn.net/zhangphil/article/details/88741024

https://blog.csdn.net/zhangphil/article/details/91060040

https://blog.csdn.net/zhangphil/article/details/90042388


Android平台自带的层次状态机实现的源代码


import android.os.Message;/*** {@hide}** The interface for implementing states in a {@link StateMachine}*/public interface IState {/*** Returned by processMessage to indicate the the message was processed.*/static final boolean HANDLED = true;/*** Returned by processMessage to indicate the the message was NOT processed.*/static final boolean NOT_HANDLED = false;/*** Called when a state is entered.*/void enter();/*** Called when a state is exited.*/void exit();/*** Called when a message is to be processed by the* state machine.** This routine is never reentered thus no synchronization* is needed as only one processMessage method will ever be* executing within a state machine at any given time. This* does mean that processing by this routine must be completed* as expeditiously as possible as no subsequent messages will* be processed until this routine returns.** @param msg to process* @return HANDLED if processing has completed and NOT_HANDLED*         if the message wasn't processed.*/boolean processMessage(Message msg);/*** Name of State for debugging purposes.** @return name of state.*/String getName();
}
import android.os.Message;
/*** {@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()*/@Overridepublic void enter() {}/* (non-Javadoc)* @see com.android.internal.util.IState#exit()*/@Overridepublic void exit() {}/* (non-Javadoc)* @see com.android.internal.util.IState#processMessage(android.os.Message)*/@Overridepublic 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)*/@Overridepublic String getName() {String name = getClass().getName();int lastDollar = name.lastIndexOf('$');return name.substring(lastDollar + 1);}
}
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Vector;/*** {@hide}** <p>The state machine defined here is a hierarchical state machine which processes messages* and can have states arranged hierarchically.</p>** <p>A state is a <code>State</code> object and must implement* <code>processMessage</code> and optionally <code>enter/exit/getName</code>.* The enter/exit methods are equivalent to the construction and destruction* in Object Oriented programming and are used to perform initialization and* cleanup of the state respectively. The <code>getName</code> method returns the* name of the state the default implementation returns the class name it may be* desirable to have this return the name of the state instance name instead.* In particular if a particular state class has multiple instances.</p>** <p>When a state machine is created <code>addState</code> is used to build the* hierarchy and <code>setInitialState</code> is used to identify which of these* is the initial state. After construction the programmer calls <code>start</code>* which initializes and starts the state machine. The first action the StateMachine* is to the invoke <code>enter</code> for all of the initial state's hierarchy,* starting at its eldest parent. The calls to enter will be done in the context* of the StateMachines Handler not in the context of the call to start and they* will be invoked before any messages are processed. For example, given the simple* state machine below mP1.enter will be invoked and then mS1.enter. Finally,* messages sent to the state machine will be processed by the current state,* in our simple state machine below that would initially be mS1.processMessage.</p><code>mP1/   \mS2   mS1 ----> initial state</code>* <p>After the state machine is created and started, messages are sent to a state* machine using <code>sendMessage</code> and the messages are created using* <code>obtainMessage</code>. When the state machine receives a message the* current state's <code>processMessage</code> is invoked. In the above example* mS1.processMessage will be invoked first. The state may use <code>transitionTo</code>* to change the current state to a new state</p>** <p>Each state in the state machine may have a zero or one parent states and if* a child state is unable to handle a message it may have the message processed* by its parent by returning false or NOT_HANDLED. If a message is never processed* <code>unhandledMessage</code> will be invoked to give one last chance for the state machine* to process the message.</p>** <p>When all processing is completed a state machine may choose to call* <code>transitionToHaltingState</code>. When the current <code>processingMessage</code>* returns the state machine will transfer to an internal <code>HaltingState</code>* and invoke <code>halting</code>. Any message subsequently received by the state* machine will cause <code>haltedProcessMessage</code> to be invoked.</p>** <p>If it is desirable to completely stop the state machine call <code>quit</code> or* <code>abort</code>. These will call <code>exit</code> of the current state and its parents, call* <code>onQuiting</code> and then exit Thread/Loopers.</p>** <p>In addition to <code>processMessage</code> each <code>State</code> has* an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>** <p>Since the states are arranged in a hierarchy transitioning to a new state* causes current states to be exited and new states to be entered. To determine* the list of states to be entered/exited the common parent closest to* the current state is found. We then exit from the current state and its* parent's up to but not including the common parent state and then enter all* of the new states below the common parent down to the destination state.* If there is no common parent all states are exited and then the new states* are entered.</p>** <p>Two other methods that states can use are <code>deferMessage</code> and* <code>sendMessageAtFrontOfQueue</code>. The <code>sendMessageAtFrontOfQueue</code> sends* a message but places it on the front of the queue rather than the back. The* <code>deferMessage</code> causes the message to be saved on a list until a* transition is made to a new state. At which time all of the deferred messages* will be put on the front of the state machine queue with the oldest message* at the front. These will then be processed by the new current state before* any other messages that are on the queue or might be added later. Both of* these are protected and may only be invoked from within a state machine.</p>** <p>To illustrate some of these properties we'll use state machine with an 8* state hierarchy:</p><code>mP0/   \mP1   mS0/   \mS2   mS1/  \    \mS3  mS4  mS5  ---> initial state</code>* <p>After starting mS5 the list of active states is mP0, mP1, mS1 and mS5.* So the order of calling processMessage when a message is received is mS5,* mS1, mP1, mP0 assuming each processMessage indicates it can't handle this* message by returning false or NOT_HANDLED.</p>** <p>Now assume mS5.processMessage receives a message it can handle, and during* the handling determines the machine should change states. It could call* transitionTo(mS4) and return true or HANDLED. Immediately after returning from* processMessage the state machine runtime will find the common parent,* which is mP1. It will then call mS5.exit, mS1.exit, mS2.enter and then* mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So* when the next message is received mS4.processMessage will be invoked.</p>** <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.* It responds with "Hello World" being printed to the log for every message.</p><code>class HelloWorld extends StateMachine {HelloWorld(String name) {super(name);addState(mState1);setInitialState(mState1);}public static HelloWorld makeHelloWorld() {HelloWorld hw = new HelloWorld("hw");hw.start();return hw;}class State1 extends State {@Override public boolean processMessage(Message message) {Log.d(TAG, "Hello World");return HANDLED;}}State1 mState1 = new State1();}void testHelloWorld() {HelloWorld hw = makeHelloWorld();hw.sendMessage(hw.obtainMessage());}</code>* <p>A more interesting state machine is one with four states* with two independent parent states.</p><code>mP1      mP2/   \mS2   mS1</code>* <p>Here is a description of this state machine using pseudo code.</p><code>state mP1 {enter { log("mP1.enter"); }exit { log("mP1.exit");  }on msg {CMD_2 {send(CMD_3);defer(msg);transitonTo(mS2);return HANDLED;}return NOT_HANDLED;}}INITIALstate mS1 parent mP1 {enter { log("mS1.enter"); }exit  { log("mS1.exit");  }on msg {CMD_1 {transitionTo(mS1);return HANDLED;}return NOT_HANDLED;}}state mS2 parent mP1 {enter { log("mS2.enter"); }exit  { log("mS2.exit");  }on msg {CMD_2 {send(CMD_4);return HANDLED;}CMD_3 {defer(msg);transitionTo(mP2);return HANDLED;}return NOT_HANDLED;}}state mP2 {enter {log("mP2.enter");send(CMD_5);}exit { log("mP2.exit"); }on msg {CMD_3, CMD_4 { return HANDLED; }CMD_5 {transitionTo(HaltingState);return HANDLED;}return NOT_HANDLED;}}</code>* <p>The implementation is below and also in StateMachineTest:</p><code>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 hierarchyaddState(mP1);addState(mS1, mP1);addState(mS2, mP1);addState(mP2);// Set the initial statesetInitialState(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_3sendMessage(obtainMessage(CMD_3));deferMessage(message);transitionTo(mS2);retVal = HANDLED;break;default:// Any message we don't understand in this state invokes unhandledMessageretVal = 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 calledtransitionTo(mS1);return HANDLED;} else {// Let parent process all other messagesreturn 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");}}@Overridevoid 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 handledhsm.wait();} catch (InterruptedException e) {Log.e(TAG, "exception while waiting " + e.getMessage());}}</code>* <p>The output is:</p><code>D/hsm1    ( 1999): makeHsm1 ED/hsm1    ( 1999): ctor ED/hsm1    ( 1999): ctor XD/hsm1    ( 1999): mP1.enterD/hsm1    ( 1999): mS1.enterD/hsm1    ( 1999): makeHsm1 XD/hsm1    ( 1999): mS1.processMessage what=1D/hsm1    ( 1999): mS1.exitD/hsm1    ( 1999): mS1.enterD/hsm1    ( 1999): mS1.processMessage what=2D/hsm1    ( 1999): mP1.processMessage what=2D/hsm1    ( 1999): mS1.exitD/hsm1    ( 1999): mS2.enterD/hsm1    ( 1999): mS2.processMessage what=2D/hsm1    ( 1999): mS2.processMessage what=3D/hsm1    ( 1999): mS2.exitD/hsm1    ( 1999): mP1.exitD/hsm1    ( 1999): mP2.enterD/hsm1    ( 1999): mP2.processMessage what=3D/hsm1    ( 1999): mP2.processMessage what=4D/hsm1    ( 1999): mP2.processMessage what=5D/hsm1    ( 1999): mP2.exitD/hsm1    ( 1999): halting</code>*/
public class StateMachine {private static final String TAG = "StateMachine";private String mName;/** Message.what value when quitting */private static final int SM_QUIT_CMD = -1;/** Message.what value when initializing */private static final int SM_INIT_CMD = -2;/*** Convenience constant that maybe returned by processMessage* to indicate the the message was processed and is not to be* processed by parent states*/public static final boolean HANDLED = true;/*** Convenience constant that maybe returned by processMessage* to indicate the the message was NOT processed and is to be* processed by parent states*/public static final boolean NOT_HANDLED = false;/*** StateMachine logging record.* {@hide}*/public static class LogRec {private long mTime;private int mWhat;private String mInfo;private State mState;private State mOrgState;/*** Constructor** @param msg* @param state that handled the message* @param orgState is the first state the received the message but* did not processes the message.*/LogRec(Message msg, String info, State state, State orgState) {update(msg, info, state, orgState);}/*** Update the information in the record.* @param state that handled the message* @param orgState is the first state the received the message but* did not processes the message.*/public void update(Message msg, String info, State state, State orgState) {mTime = System.currentTimeMillis();mWhat = (msg != null) ? msg.what : 0;mInfo = info;mState = state;mOrgState = orgState;}/*** @return time stamp*/public long getTime() {return mTime;}/*** @return msg.what*/public long getWhat() {return mWhat;}/*** @return the command that was executing*/public String getInfo() {return mInfo;}/*** @return the state that handled this message*/public State getState() {return mState;}/*** @return the original state that received the message.*/public State getOriginalState() {return mOrgState;}/*** @return as string*/public String toString(StateMachine sm) {StringBuilder sb = new StringBuilder();sb.append("time=");Calendar c = Calendar.getInstance();c.setTimeInMillis(mTime);sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));sb.append(" state=");sb.append(mState == null ? "<null>" : mState.getName());sb.append(" orgState=");sb.append(mOrgState == null ? "<null>" : mOrgState.getName());sb.append(" what=");String what = sm.getWhatToString(mWhat);if (TextUtils.isEmpty(what)) {sb.append(mWhat);sb.append("(0x");sb.append(Integer.toHexString(mWhat));sb.append(")");} else {sb.append(what);}if ( ! TextUtils.isEmpty(mInfo)) {sb.append(" ");sb.append(mInfo);}return sb.toString();}}/*** A list of log records including messages recently processed by the state machine.** The class maintains a list of log records including messages* recently processed. The list is finite and may be set in the* constructor or by calling setSize. The public interface also* includes size which returns the number of recent records,* count which is the number of records processed since the* the last setSize, get which returns a record and* add which adds a record.*/private static class LogRecords {private static final int DEFAULT_SIZE = 20;private Vector<LogRec> mLogRecords = new Vector<LogRec>();private int mMaxSize = DEFAULT_SIZE;private int mOldestIndex = 0;private int mCount = 0;/*** private constructor use add*/private LogRecords() {}/*** Set size of messages to maintain and clears all current records.** @param maxSize number of records to maintain at anyone time.*/synchronized void setSize(int maxSize) {mMaxSize = maxSize;mCount = 0;mLogRecords.clear();}/*** @return the number of recent records.*/synchronized int size() {return mLogRecords.size();}/*** @return the total number of records processed since size was set.*/synchronized int count() {return mCount;}/*** Clear the list of records.*/synchronized void cleanup() {mLogRecords.clear();}/*** @return the information on a particular record. 0 is the oldest* record and size()-1 is the newest record. If the index is to* large null is returned.*/synchronized LogRec get(int index) {int nextIndex = mOldestIndex + index;if (nextIndex >= mMaxSize) {nextIndex -= mMaxSize;}if (nextIndex >= size()) {return null;} else {return mLogRecords.get(nextIndex);}}/*** Add a processed message.** @param msg* @param messageInfo to be stored* @param state that handled the message* @param orgState is the first state the received the message but* did not processes the message.*/synchronized void add(Message msg, String messageInfo, State state, State orgState) {mCount += 1;if (mLogRecords.size() < mMaxSize) {mLogRecords.add(new LogRec(msg, messageInfo, state, orgState));} else {LogRec pmi = mLogRecords.get(mOldestIndex);mOldestIndex += 1;if (mOldestIndex >= mMaxSize) {mOldestIndex = 0;}pmi.update(msg, messageInfo, state, orgState);}}}private static class SmHandler extends Handler {/** 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*/@Overridepublic 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<State, StateInfo> mStateInfo =new HashMap<State, StateInfo>();/** The initial state that will process the first message */private State mInitialState;/** The destination state when transitionTo has been invoked */private State mDestState;/** The list of deferred messages */private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();/*** State entered when transitionToHaltingState is called.*/private class HaltingState extends State {@Overridepublic boolean processMessage(Message msg) {mSm.haltedProcessMessage(msg);return true;}}/*** State entered when a valid quit message is handled.*/private class QuittingState extends State {@Overridepublic boolean processMessage(Message msg) {return NOT_HANDLED;}}/*** 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.*/@Overridepublic final void handleMessage(Message msg) {if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);/** Save the current message */mMsg = msg;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 (mDbg) Log.d(TAG, "handleMessage: X");}/*** Do any transitions*/private void performTransitions() {/*** 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 destState = null;while (mDestState != null) {if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");/*** Save mDestState locally and set to null* to know if enter/exit use transitionTo.*/destState = mDestState;mDestState = null;/*** 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();}/*** 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();}}}/*** Cleanup all the static variables and the looper after the SM has been quit.*/private final void cleanupAfterQuitting() {if (mSm.mSmThread != null) {// If we made the thread then quit looper which stops the thread.getLooper().quit();mSm.mSmThread = null;}mSm.mSmHandler = null;mSm = null;mMsg = null;mLogRecords.cleanup();mStateStack = null;mTempStateStack = null;mStateInfo.clear();mInitialState = null;mDestState = null;mDeferredMessages.clear();}/*** Complete the construction of the state machine.*/private final void completeConstruction() {if (mDbg) Log.d(TAG, "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) Log.d(TAG, "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) Log.d(TAG, "completeConstruction: 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.*/private final void processMsg(Message msg) {StateInfo curStateInfo = mStateStack[mStateStackTopIndex];if (mDbg) {Log.d(TAG, "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) {Log.d(TAG, "processMsg: " + curStateInfo.state.getName());}}/*** Record that we processed the message*/if (mSm.recordLogRec(msg)) {if (curStateInfo != null) {State orgState = mStateStack[mStateStackTopIndex].state;mLogRecords.add(msg, mSm.getLogRecString(msg), curStateInfo.state,orgState);} else {mLogRecords.add(msg, mSm.getLogRecString(msg), null, null);}}}}/*** 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) Log.d(TAG, "invokeExitMethods: " + curState.getName());curState.exit();mStateStack[mStateStackTopIndex].active = false;mStateStackTopIndex -= 1;}}/*** 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) Log.d(TAG, "invokeEnterMethods: " + mStateStack[i].state.getName());mStateStack[i].state.enter();mStateStack[i].active = true;}}/*** Move the deferred message to the front of the message queue.*/private final void moveDeferredMessageAtFrontOfQueue() {/*** The oldest messages on the deferred list must be at* the front of the queue so start at the back, which* as the most resent message and end with the oldest* messages at the front of the queue.*/for (int i = mDeferredMessages.size() - 1; i >= 0; i-- ) {Message curMsg = mDeferredMessages.get(i);if (mDbg) Log.d(TAG, "moveDeferredMessageAtFrontOfQueue; what=" + curMsg.what);sendMessageAtFrontOfQueue(curMsg);}mDeferredMessages.clear();}/*** 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) 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;}/*** 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) {Log.d(TAG, "setupTempStateStackWithStatesToEnter: X mTempStateStackCount="+ mTempStateStackCount + ",curStateInfo: " + curStateInfo);}return curStateInfo;}/*** Initialize StateStack to mInitialState.*/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;}// Empty the StateStackmStateStackTopIndex = -1;moveTempStateStackToStateStack();}/*** @return current message*/private final Message getCurrentMessage() {return mMsg;}/*** @return current state*/private final IState getCurrentState() {return mStateStack[mStateStackTopIndex].state;}/*** 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) {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) {// 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) Log.d(TAG, "addStateInternal: X stateInfo: " + stateInfo);return stateInfo;}/*** Constructor** @param looper for dispatching messages* @param sm the hierarchical state machine*/private SmHandler(Looper looper, StateMachine sm) {super(looper);mSm = sm;addState(mHaltingState, null);addState(mQuittingState, null);}/** @see StateMachine#setInitialState(State) */private final void setInitialState(State initialState) {if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName());mInitialState = initialState;}/** @see StateMachine#transitionTo(IState) */private final void transitionTo(IState destState) {mDestState = (State) destState;if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());}/** @see StateMachine#deferMessage(Message) */private final void deferMessage(Message msg) {if (mDbg) Log.d(TAG, "deferMessage: msg=" + msg.what);/* Copy the "msg" to "newMsg" as "msg" will be recycled */Message newMsg = obtainMessage();newMsg.copyFrom(msg);mDeferredMessages.add(newMsg);}/** @see StateMachine#quit() */private final void quit() {if (mDbg) Log.d(TAG, "quit:");sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));}/** @see StateMachine#quitNow() */private final void quitNow() {if (mDbg) Log.d(TAG, "abort:");sendMessageAtFrontOfQueue(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));}/** Validate that the message was sent by quit or abort. */private final boolean isQuit(Message msg) {return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);}/** @see StateMachine#isDbg() */private final boolean isDbg() {return mDbg;}/** @see StateMachine#setDbg(boolean) */private final void setDbg(boolean dbg) {mDbg = dbg;}}private SmHandler mSmHandler;private HandlerThread mSmThread;/*** Initialize.** @param looper for this state machine* @param name of the state machine*/private void initStateMachine(String name, Looper looper) {mName = name;mSmHandler = new SmHandler(looper, this);}/*** Constructor creates a StateMachine with its own thread.** @param name of the state machine*/protected StateMachine(String name) {mSmThread = new HandlerThread(name);mSmThread.start();Looper looper = mSmThread.getLooper();initStateMachine(name, looper);}/*** Constructor creates a StateMachine using the looper.** @param name of the state machine*/protected StateMachine(String name, Looper looper) {initStateMachine(name, looper);}/*** Add a new state to the state machine* @param state the state to add* @param parent the parent of state*/protected final void addState(State state, State parent) {mSmHandler.addState(state, parent);}/*** @return current message*/protected final Message getCurrentMessage() {return mSmHandler.getCurrentMessage();}/*** @return current state*/protected final IState getCurrentState() {return mSmHandler.getCurrentState();}/*** 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);}/*** Set the initial state. This must be invoked before* and messages are sent to the state machine.** @param initialState is the state which will receive the first message.*/protected final void setInitialState(State initialState) {mSmHandler.setInitialState(initialState);}/*** 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);}/*** transition to halt state. Upon returning* from processMessage we will exit all current* states, execute the onHalting() method and then* for all subsequent messages haltedProcessMessage* will be called.*/protected final void transitionToHaltingState() {mSmHandler.transitionTo(mSmHandler.mHaltingState);}/*** Defer this message until next state transition.* Upon transitioning all deferred messages will be* placed on the queue and reprocessed in the original* order. (i.e. The next state the oldest messages will* be processed first)** @param msg is deferred until the next transition.*/protected final void deferMessage(Message msg) {mSmHandler.deferMessage(msg);}/*** Called when message wasn't handled** @param msg that couldn't be handled.*/protected void unhandledMessage(Message msg) {if (mSmHandler.mDbg) Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);}/*** Called for any message that is received after* transitionToHalting is called.*/protected void haltedProcessMessage(Message msg) {}/*** This will be called once after handling a message that called* transitionToHalting. All subsequent messages will invoke* {@link StateMachine#haltedProcessMessage(Message)}*/protected void onHalting() {}/*** This will be called once after a quit message that was NOT handled by* the derived StateMachine. The StateMachine will stop and any subsequent messages will be* ignored. In addition, if this StateMachine created the thread, the thread will* be stopped after this method returns.*/protected void onQuitting() {}/*** @return the name*/public final String getName() {return mName;}/*** Set number of log records to maintain and clears all current records.** @param maxSize number of messages to maintain at anyone time.*/public final void setLogRecSize(int maxSize) {mSmHandler.mLogRecords.setSize(maxSize);}/*** @return number of log records*/public final int getLogRecSize() {return mSmHandler.mLogRecords.size();}/*** @return the total number of records processed*/public final int getLogRecCount() {return mSmHandler.mLogRecords.count();}/*** @return a log record*/public final LogRec getLogRec(int index) {return mSmHandler.mLogRecords.get(index);}/*** Add the string to LogRecords.** @param string*/protected void addLogRec(String string) {mSmHandler.mLogRecords.add(null, string, null, null);}/*** Add the string and state to LogRecords** @param string* @param state current state*/protected void addLogRec(String string, State state) {mSmHandler.mLogRecords.add(null, string, state, null);}/*** @return true if msg should be saved in the log, default is true.*/protected boolean recordLogRec(Message msg) {return true;}/*** Return a string to be logged by LogRec, default* is an empty string. Override if additional information is desired.** @param msg that was processed* @return information to be logged as a String*/protected String getLogRecString(Message msg) {return "";}/*** @return the string for msg.what*/protected String getWhatToString(int what) {return null;}/*** @return Handler*/public final Handler getHandler() {return mSmHandler;}/*** Get a message and set Message.target = this.** @return message or null if SM has quit*/public final Message obtainMessage(){if (mSmHandler == null) return null;return Message.obtain(mSmHandler);}/*** Get a message and set Message.target = this and what** @param what is the assigned to Message.what.* @return message or null if SM has quit*/public final Message obtainMessage(int what) {if (mSmHandler == null) return null;return Message.obtain(mSmHandler, what);}/*** Get a message and set Message.target = this,* what and obj.** @param what is the assigned to Message.what.* @param obj is assigned to Message.obj.* @return message or null if SM has quit*/public final Message obtainMessage(int what, Object obj){if (mSmHandler == null) return null;return Message.obtain(mSmHandler, what, obj);}/*** Get a message and set Message.target = this,* what, arg1 and arg2** @param what  is assigned to Message.what* @param arg1  is assigned to Message.arg1* @param arg2  is assigned to Message.arg2* @return  A Message object from the global pool or null if*          SM has quit*/public final Message obtainMessage(int what, int arg1, int arg2){if (mSmHandler == null) return null;return Message.obtain(mSmHandler, what, arg1, arg2);}/*** Get a message and set Message.target = this,* what, arg1, arg2 and obj** @param what  is assigned to Message.what* @param arg1  is assigned to Message.arg1* @param arg2  is assigned to Message.arg2* @param obj is assigned to Message.obj* @return  A Message object from the global pool or null if*          SM has quit*/public final Message obtainMessage(int what, int arg1, int arg2, Object obj){if (mSmHandler == null) return null;return Message.obtain(mSmHandler, what, arg1, arg2, obj);}/*** Enqueue a message to this state machine.*/public final void sendMessage(int what) {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;mSmHandler.sendMessage(obtainMessage(what));}/*** Enqueue a message to this state machine.*/public final void sendMessage(int what, Object obj) {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;mSmHandler.sendMessage(obtainMessage(what,obj));}/*** Enqueue a message to this state machine.*/public final void sendMessage(Message msg) {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;mSmHandler.sendMessage(msg);}/*** Enqueue a message to this state machine after a delay.*/public final void sendMessageDelayed(int what, long delayMillis) {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);}/*** Enqueue a message to this state machine after a delay.*/public final void sendMessageDelayed(int what, Object obj, long delayMillis) {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);}/*** Enqueue a message to this state machine after a delay.*/public final void sendMessageDelayed(Message msg, long delayMillis) {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;mSmHandler.sendMessageDelayed(msg, delayMillis);}/*** Enqueue a message to the front of the queue for this state machine.* Protected, may only be called by instances of StateMachine.*/protected final void sendMessageAtFrontOfQueue(int what, Object obj) {mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));}/*** Enqueue a message to the front of the queue for this state machine.* Protected, may only be called by instances of StateMachine.*/protected final void sendMessageAtFrontOfQueue(int what) {mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));}/*** Enqueue a message to the front of the queue for this state machine.* Protected, may only be called by instances of StateMachine.*/protected final void sendMessageAtFrontOfQueue(Message msg) {mSmHandler.sendMessageAtFrontOfQueue(msg);}/*** Removes a message from the message queue.* Protected, may only be called by instances of StateMachine.*/protected final void removeMessages(int what) {mSmHandler.removeMessages(what);}/*** Quit the state machine after all currently queued up messages are processed.*/protected final void quit() {// mSmHandler can be null if the state machine is already stopped.if (mSmHandler == null) return;mSmHandler.quit();}/*** Quit the state machine immediately all currently queued messages will be discarded.*/protected final void quitNow() {// mSmHandler can be null if the state machine is already stopped.if (mSmHandler == null) return;mSmHandler.quitNow();}/*** @return if debugging is enabled*/public boolean isDbg() {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return false;return mSmHandler.isDbg();}/*** Set debug enable/disabled.** @param dbg is true to enable debugging.*/public void setDbg(boolean dbg) {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;mSmHandler.setDbg(dbg);}/*** Start the state machine.*/public void start() {// mSmHandler can be null if the state machine has quit.if (mSmHandler == null) return;/** Send the complete construction message */mSmHandler.completeConstruction();}/*** Dump the current state.** @param fd* @param pw* @param args*/public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {pw.println(getName() + ":");pw.println(" total records=" + getLogRecCount());for (int i=0; i < getLogRecSize(); i++) {pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString(this));pw.flush();}pw.println("curState=" + getCurrentState().getName());}public void log(String s){System.out.println(s);}
}

Android系统自带的层次状态机StateMachine(Hierarchical State Machine)相关推荐

  1. Android官方实现的层次状态机Hierarchical State Machine源代码

    Android官方内部的源代码中实现了一套层次状态机(Hierarchical State Machine),总共有三个代码文件:IState.java , State.java, StateMach ...

  2. Android系统自带样式(android:theme)(转)

    Android系统自带样式(android:theme)(转) android:theme="@android:style/Theme.Dialog" : Activity显示为对 ...

  3. 可以删除的android系统自带程序—详细列表【转】

    android系统自带的很多程序平时根本用不到或很少用到,但是它们有的却在你开机之后默默地在后台运行着,既占内存又消耗电量,这时我们就需要把他们一一杀掉!但是很多人不明白这些软件都是些什么,哪些能删哪 ...

  4. android 下拉刷新 组件,android系统自带下拉刷新控件的实现

    android系统自带的下拉刷新控件SwipeRefreshLayout位于android.support.v4.widget包下,实现步骤如下: 1.在布局文件中添加该控件,该控件一般作为父控件,而 ...

  5. tts android,Android系统自带的TTS实现语音播报

    Android系统自带的TTS实现语音播报(类似支付宝到账xx元)(这里写自定义目录标题) 以按钮的点击事件来实现: public class MainActivity extends Activit ...

  6. Android 系统自带图标

    Android 系统自带了很多图标,小巧而精美 图库目录:android-sdk/platforms/android-*/data/res/drawable-* 使用方式: xml >> ...

  7. 隐藏android系统自带应用程序

    android系统自带的程序,有些是可以删除的,有些是不能强制删除的. 1.对于能够删除的程序,如果想在程序列表中看不到它,直接删除源程序即可. 2.对于不能删除的程序,要想在系统的程序列表中隐藏它, ...

  8. android 系统自带主题样式及自定义主题样式

    From: http://blog.csdn.net/dawanganban/article/details/17732701 http://www.cnblogs.com/bluestorm/arc ...

  9. android手机自带表情符号,android系统自带Emoji表情与表情描述互相转换

    前言 Emoji表情几乎都是有表情描述的,如愤怒的表情会有[愤怒],这样的描述,这里就是举了个列子,至于是什么样的表现形势,这是可以自己定义的.那么这样,一个表情带一个表情描述的好处就是,当我们发送信 ...

最新文章

  1. C++ 重载运算符 operator
  2. xml常用操作(js、sql、vb)
  3. html多重边框,中间空白,CSS揭秘之多重边框的实现
  4. leetcode 26 删除排序数组中的重复项
  5. 天大18年c语言离线作业,天大18秋C语言程序设计在线作业一辅导资料.docx-资源下载在线文库www.lddoc.cn...
  6. sklearn的逻辑回归
  7. steam夏日促销悄然开始,用Python爬取排行榜上的游戏打折信息
  8. ps教程分享:一定要记住这20种PS技术!
  9. 深入理解Java虚拟机--笔记1
  10. redis 如何查看某个库的key_如何发现 Redis 热点 Key ,解决方案有哪些?
  11. 我的公司不是家 【联想员工亲历联想大裁员】
  12. cad线段总和lisp_autocadlisp统计多段线方法
  13. <POSTGRESQL修炼之道:从小工到专家>之逻辑结构管理(1-5节)
  14. 个人项目-20组刘鼎事后诸葛亮会议
  15. ar军事作战数字三维仿真沙盘开发
  16. Oracle 查询库文件信息
  17. 贝叶斯分析助你成为优秀的调参侠:自动化搜索物理模型的参数空间
  18. windows 10 HBase保姆级安装教程
  19. 直播教育平台开发—老师学生上课学习的好帮手
  20. 计算机组装与维修第3版,零起点计算机组装与维护_第3课.pdf

热门文章

  1. 为实体提供枚举类型的支持
  2. 张北川:命名数据网络(NDN)
  3. linux怎么共享存储,什么是ISCSI,及Linux下怎么样通过ISCSI实现共享存储
  4. 【单片机基础】89C52单片机串口通信
  5. Android Studio 实现跑马灯微项目实例
  6. [No00002A]26个英语字母的原始象形意义、含义、产生及发展历史
  7. 因为你还没有遇到给你三颗痣的人
  8. 产品打磨日记-给SA交待的任务
  9. 计算机科学计算的方面,计算机的科学计算功能在工程领域中的应用.doc
  10. 大学十年__献给计算机专业的所有学子