Android状态机源码分析

在Android系统中,经常使用状态机来处理不同状态下的行为动作。状态机是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。Android系统的StateMachine机制是一个State模式的应用,StateMachine是一个分层处理消息的状态机,并且是能够有分层排列状态。


构造状态机

StateMachine的构造函数都是protected类型,不能实例化,都是由其子类进行初始化操作。StateMachine有两个重载的构造函数,一个是通过指定消息循环对象来构造状态机

protected StateMachine(String name, Looper looper) {
	initStateMachine(name, looper);
}

另一个则是直接启动一个消息循环线程来构造一个状态机

protected StateMachine(String name) {
	//启动一个消息循环线程
	mSmThread = new HandlerThread(name);
	mSmThread.start();
	Looper looper = mSmThread.getLooper();
	initStateMachine(name, looper);
}
这两种构造函数都会通过initStateMachine函数来初始化该状态机

private void initStateMachine(String name, Looper looper) {
	mName = name;
	mSmHandler = new SmHandler(looper, this);
}
初始化过程比较简单,就是将状态机名称保存到成员变量mName中,同时创建SmHandler对象,SmHandler是一个Handle对象,用于派发消息。

状态机中的每个状态使用State来封装,对于每个状态的信息又采用StateInfo来描述

StateMachine三个内部类:
1.ProcessedMessageInfo:保存已处理消息的信息;
2.ProcessedMessages:存储StateMachine最近处理的一些消息,需要保存最近处理的消息条数默认20,可以用户自己设定最大数目;
3.SmHandle是消息处理派发和状态控制切换的核心,运行在单独的线程上;

SmHandle成员变量定义:


ProcessedMessages用于保存已处理过的消息,mStateStack和mTempStateStack是一个数组栈,用于保存状态机中的链式状态关系。

mStateInfo定义为一个Hash链表,用于保存添加的所有状态。mInitialState保存初始状态,mDestState保存切换的目的状态。

建立树形层次结构状态机

在构造完一个状态机前需要向状态机中添加各种状态,StateMachine提供了addState函数来添加状态
protected final void addState(State state, State parent) {
	mSmHandler.addState(state, parent);
}
直接通过mSmHandler来完成状态添加过程
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;
}
状态添加过程其实就是为每个State创建相应的StateInfo对象,通过该对象来建立各个状态之间的关系,并且一个State-StateInfo键值对的方式保存到mStateInfo Hash表中。StateInfo就是包装State组成一个Node,建立State的父子关系;mStateInfo =new HashMap<State, StateInfo>();用来保存State Machine中的所有State,可以按照树形层次结构组织状态机中的所有状态
接下来介绍构造以下树形状态机的过程:
sm.addState(S0,null);
sm.addState(S1,S0);
sm.addState(S2,S0);
sm.addState(S3,S1);
sm.addState(S4,S1);
sm.addState(S5,S2);
sm.addState(S6,S2);
sm.addState(S7,S2);
setInitialState(S4);      //设置初始状态
1.添加根节点状态过程
对于根节点状态的加入sm.addState(S0,null)代码执行如下:
private final StateInfo addState(S0,null) 
{
	StateInfo parentStateInfo = null;
	//状态S0还未加入状态机中
	StateInfo stateInfo = mStateInfo.get(state);
  if (stateInfo == null) {
    //创建State详细信息对象
    stateInfo = new StateInfo();
		//将S0加入状态机中
    mStateInfo.put(state, stateInfo);
  }
  //设置状态S0的状态信息
  	stateInfo.state = state;
	//S0的父节点为null
  	stateInfo.parentStateInfo = parentStateInfo;
  stateInfo.active = false;
  return stateInfo;
}
2.添加树枝节点状态过程
对于子节点S1状态的加入过程如下sm.addState(S1,S0):
private final StateInfo addState(State state, State parent) 
{
  StateInfo parentStateInfo = null;
	//状态S0在前面已经加入状态机中了
  if (parent != null) {
    //获取S0详细信息 StateInfo
		parentStateInfo = mStateInfo.get(parent);
		//因为S0已经存在于状态机中,因此这里不为空
    if (parentStateInfo == null) {
      //当前状态父状态未加入到StateMachine中,递归先加入其父State
      parentStateInfo = addState(parent, null);
    }
  }
  //从状态机中得到S1的状态信息,由于S1还为加入状态机,因此得到的结果为空
  StateInfo stateInfo = mStateInfo.get(state);
  if (stateInfo == null) {
    //创建State详细信息对象
    stateInfo = new StateInfo();
		//将S1加入状态机中
    mStateInfo.put(state, stateInfo);
  }
  //S1的状态信息刚创建,还没有为其设置父节点,因此其父节点为空
  if ((stateInfo.parentStateInfo != null) &&
    (stateInfo.parentStateInfo != parentStateInfo)) {
      throw new RuntimeException("state already added");
  }
  //设置状态S1的状态信息
  	stateInfo.state = state;
	//S1的父节点为S0
  	stateInfo.parentStateInfo = parentStateInfo;
  stateInfo.active = false;
  return stateInfo;
}
对于其他树枝节点的添加过程类似,这里不在介绍,最后保存在mStateInfo表中的所有状态之间就建立了以下树形关系:


启动状态机

当向状态机中添加完所有状态时,通过函数start函数来启动状态机
public void start() {
	// mSmHandler can be null if the state machine has quit.
	if (mSmHandler == null) return;
	/** Send the complete construction message */
	mSmHandler.completeConstruction();
}
调用mSmHandler的completeConstruction函数来完成状态机的构造完成处理
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,mTempStateStack状态栈
	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");
}
计算状态树的最大深度方法:
1.遍历状态树中的所有节点;
2.以每一个节点为起始点,根据节点父子关系查找到根节点;
3.累计起始节点到根节点之间的节点个数;查找最大的节点个数。
根据查找到的树的最大节点个数来创建两个状态堆栈,并调用函数setupInitialStateStack来填充该堆栈
private final void setupInitialStateStack() {
	//在mStateInfo中取得初始状态mInitialState对应的StateInfo
	StateInfo curStateInfo = mStateInfo.get(mInitialState);
	//从初始状态mInitialState开始根据父子关系填充mTempStateStack堆栈
	for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
		mTempStateStack[mTempStateStackCount] = curStateInfo;
		curStateInfo = curStateInfo.parentStateInfo;
	}
	// Empty the StateStack
	mStateStackTopIndex = -1;
	//将mTempStateStack中的状态按反序方式移动到mStateStack栈中
	moveTempStateStackToStateStack();
}
从上图可以看出,当初始状态为S4时,保存到mTempStateStack的节点为:
mTempStateStack={S4,S1,S0}
mTempStateStackCount = 3;
mStateStackTopIndex = -1;
然后调用函数moveTempStateStackToStateStack将节点以反序方式保存到mStateStack中
private final int moveTempStateStackToStateStack() {
	//startingIndex= 0
	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;
	return startingIndex;
}
mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
初始化完状态栈后,SmHandler将向消息循环中发送一个SM_INIT_CMD消息
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj))
该消息对应的处理如下:
else if (!mIsConstructionCompleted &&(mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
	mIsConstructionCompleted = true;
	invokeEnterMethods(0);
}
performTransitions();
消息处理过程首先调用invokeEnterMethods函数将mStateStack栈中的所有状态设置为激活状态,同时调用每一个状态的enter()函数
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;
	}
}
最后调用performTransitions函数来切换状态,同时设置mIsConstructionCompleted为true,表示状态机已经启动完成,SmHandler在以后的消息处理过程中就不在重新启动状态机了。

状态切换


SmHandler在处理每个消息时都会调用performTransitions来检查状态切换
private synchronized void performTransitions() {
  while (mDestState != null){
    //当前状态切换了 存在于mStateStack中的State需要改变
    //仍然按照链式父子关系来存储
    //先从当前状态S3找到 最近的被激活的parent状态S0
    //未被激活的全部保存起来(S3,S1) 返回S0
    StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
    //将mStateStack中 不属于当前状态(S3),
    //关系链上的State(S5,S2)退出(执行exit方法)
    invokeExitMethods(commonStateInfo);
    //将S3关系链 加入到栈中(S3,S1)
    int stateStackEnteringIndex = moveTempStateStackToStateStack();
    //将新加入到mStateStack中 未被激活的State激活(S3,S1)
    invokeEnterMethods(stateStackEnteringIndex);
    //将延迟的消息移动到消息队列的前面,以便快速得到处理               
    moveDeferredMessageAtFrontOfQueue();
  }
}
首先介绍一下状态切换的思路:

以上图中,初始状态为S4,现在目标状态mDestState被设置为S7。前面介绍了保存在mStateStack数组中的节点为:
mStateStack={S0,S1,S4}
mStateStackTopIndex = 2
这是以初始状态节点为起点遍历节点树得到的节点链表。
现在要切换到S7状态节点,则以S7为起始节点,同样遍历状态节点树,查找未激活的所有节点,并保存到mTempStateStack数组中
mTempStateStack={S7,S2,S0}
mTempStateStackCount = 3
接着调用mStateStack中除S0节点外的其他所有节点的exit函数,并且将每个状态节点设置为未激活状态,因此S4,S1被设置为未激活状态;将切换后的状态节点链表mTempStateStack移动到mStateStack,
mStateStack={S0,S2,S7}
mStateStackTopIndex = 2
并调用节点S2,S7的enter函数,同时设置为激活状态。
理解了整个状态切换过程后,就能更好地理解代码,首先根据目标状态建立状态节点链路表
private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
	mTempStateStackCount = 0;
	StateInfo curStateInfo = mStateInfo.get(destState);
	do {
		mTempStateStack[mTempStateStackCount++] = curStateInfo;
		if (curStateInfo != null) {
			curStateInfo = curStateInfo.parentStateInfo;
		}
	} while ((curStateInfo != null) && !curStateInfo.active);
	return curStateInfo;
}
然后弹出mStateStack中保存的原始状态
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;
	}
}
将新建立的状态节点链表保存到mStateStack栈中
private final int moveTempStateStackToStateStack() {
	//startingIndex= 0
	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;
	return startingIndex;
}
初始化入栈的所有新状态,并设置为激活状态
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;
	}
}
如何设置目标状态呢?StateMachine提供了transitionTo函数来切换状态
protected final void transitionTo(IState destState) {
	mSmHandler.transitionTo(destState);
}
该函数直接调用SmHandler的transitionTo函数来实现,SmHandler的transitionTo函数定义如下:
private final void transitionTo(IState destState) {
	mDestState = (State) destState;
	if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
}
这里只是简单地设置了mDestState变量,并未真正更新状态栈 mStateStack,在前面介绍了SmHandler在每次处理消息时都会自动更新一次mStateStack,无论mDestState变量值是否改变。由此可知目标状态的设置与状态栈的更新是异步的。

消息处理


StateMachine处理的核心就是SmHandler,就是一个Handler,运行在单独线程中。Handler是用来异步处理派发消息,这里使用Handler管理各个状态,派发消息处理到各个状态中去执行。StateMachine提供了多个消息发送接口,通过这些接口函数可以将消息发送到SmHandler中。
public final void sendMessage(int what) {
	// mSmHandler can be null if the state machine has quit.
	if (mSmHandler == null) return;
	mSmHandler.sendMessage(obtainMessage(what));
}
SmHandler将处理通过SmHandler发送的消息,处理过程如下:
public final void handleMessage(Message msg) {
	/** Save the current message */
	mMsg = msg;
	if (mIsConstructionCompleted) {
		//派发当前消息到state中去处理
		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);
	}
	//消息处理完毕更新mStateStack
	performTransitions();
}
变量mIsConstructionCompleted在状态机启动完成后被设置为true,因此这里将调用processMsg函数来完成消息处理。
private final void processMsg(Message msg) {
	StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
	//如果当前状态未处理该消息
	while (!curStateInfo.state.processMessage(msg)) {
		//将消息传给当前状态的父节点处理
		curStateInfo = curStateInfo.parentStateInfo;
		if (curStateInfo == null) {
			 //当前状态无父几点,则丢弃该消息
			mSm.unhandledMessage(msg);
			if (isQuit(msg)) {
				transitionTo(mQuittingState);
			}
			break;
		}
	}
	//记录处理过的消息
	if (mSm.recordProcessedMessage(msg)) {
		if (curStateInfo != null) {
			State orgState = mStateStack[mStateStackTopIndex].state;
			mProcessedMessages.add(msg, mSm.getMessageInfo(msg), curStateInfo.state,orgState);
		} else {
			mProcessedMessages.add(msg, mSm.getMessageInfo(msg), null, null);
		}
	}
}
消息处理过程是从mStateStack栈顶派发到栈底,直到该消息被处理!

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1.整体项目采用MVC框架,是对android知识点的综合应用,用到的技术有 (activity,service,broadcast,content provider,Notification , 数据库,自定义title,自定义控件,自定义toast,widget,aidl进程间通讯, javascript和java的互相调用等) 2.介绍了listview和gridview等控件的优化技巧,提升软件的效率 3.穿插介绍了企业开发中的bug管理,mantis的使用,自动化测试robotium的使用 ,log管理. 软件的打包,混淆,反编译和三方广告的加入等技巧。 该项目主要涵盖以下几大功能: 手机防盗: 根据预设参数, 判断手机是否被盗,根据自定义协议发送手机中sim卡的信息和手机的位置信息给安全号码. 可以自定义特殊号码,拨号快速进入手机防盗功能,可远程通过短信指令,给手机设置锁屏密码, 远程锁定手机屏幕, 远程格式化手机sd卡,恢复出厂设置,极大的保护用户的隐私安全,通过aidl注册admin设备,一般用户无法卸载该程序。 通讯卫士: 来电号码归属地显示,来电归属地位置的调整,来电黑名单/短信黑名单管理. 电话短信备份和还原. 保护手机的数据和通讯安全。 软件管理: 系统软件和本地软件,可以显示软件的详细信息,启动,删除应用程序.连接获取服务器上软件的评分信息等功能。程序锁可以指定要保护的程序, 用户进入要保护的程序之前必须输入密码。 任务管理: 显示当前系统运行的进程信息 显示系统内存信息, 可以杀死某个进程,批量杀死进程,通过桌面widget 实时展现用户当前的手机内存状态。 上网管理: 显示出每个程序wifi和3g/2g 访问的上传和下载的流量,帮助用户了解程序产生的流量信息. 漫游管理. 当检查到手机处于漫游状态时会提示用户。 手机杀毒:从服务器下载最新的病毒库, 根据程序的包名和程序的数字签名识别病毒,提示用户并查杀. 恶意软件,吸费木马无处可藏。 系统优化: 清理手机缓存,提高手机性能,优化电池管理 高级工具: 自动ip拨号,手机号码归属地查询,更改归属地位置,常用号码查询。 安全专题: 1、通过0权限上传下载数据,重启手机等案例,深入讲解android沙箱,安全机制和权限模型。 2、通过分析恶意代码的提权漏洞,讲解如何维护系统的安全。 3、通过linux键盘驱动案例的讲解,分析盗号木马的原理及其实现方式。 4、恶意软件发展速度的确一日千里,安全软件也要与时俱进,世面上的手机病毒已经具有了可以杀掉安全软件的功能, 通过多进程互相守护案例,讲述如何实现安全软件的自我保护。
Android应用源码安卓源码(172个合集),可以做为你的学习参考。 365MobileSecretary v1.0.6(365手机助手AIDL) 888个经典 Logo.rar AdXmpp(Openfire+asmack+spark) .zip AidlDemo(简单aidl的例子) aidl跨进程调用.rar andbatdog电池监控.rar andbatdog监视电池.rar andricoFacebook客户端.rar Android Gamex分析报告.rar Android 英语单词记忆程序源码 AndroidPdfViewerPDF查看器.zip AndroidPlayer(仿酷狗播放器).tar androidtalk_2010_11_17【Sundy系列】全看懂了-加两年经验-语音朗读-语音识别-语音.rar Android下的信息客户端 WhisperSystems-TextSecure.zip Android与js交互.rar Android中监听电话状态.rar Android之Wifi学习教程.rar Android之用PopupWindow实现弹出菜单.rar android在wifi下手机与电脑的socket通信.rar android多线程断点下载.rar Android手机一键Root原理分析.zip Android手机的VoIP客户端 Sipdroid.rar Android有未接来电后处理(判断未接来电).rar Android模仿乐淘的应用程序分析源码.zip Android游戏源码——忍者快跑.rar Android自动发送短信.rar Android自动开关机实现.rar Android视频采集+RTSP完整代码(可用) Android远程登录含有loading登录效.zip Angle v1.0_2D游戏引擎.ZIP BOOK看遍所有UI控件.7z BrewClock闹钟.zip BTAndroidWebViewSelection(webview选择文字) cellmap v2.0 基站查询定位导航系统 .rar DialogShow.rar dialog去除边框代码.rar DocumentViewer(PDF阅读器) douBanList(滚动到底部加载新的,软缓存,懒加载) Droid Wall 手机防火墙.zip FBReader修改epub快速加载.rar FiveChess五子棋.zip Flashlight灯光.zip GetSDTree(简单SD卡文件浏览器) hotel宾馆系统.zip ImageView 图片循环跑马灯的效果.rar ipcamera-for-android 手机变成IP Camera.rar jamendo-开源在线音乐.rar jchat4android手机聊天程序.rar LoginXml.rar MineSweeper由java实现.zip miniTwitter登录界面.rar MyAppWeixin(仿微信界面) MyBrowser(简单网页浏览器) Myjob3(图片剪辑功能).rar OPENG开发的示例代码.rar OpenSudoku一个简单的九宫格数独游戏.zip OssSystem(OA系统图书管理简单版).rar Phonegap+HTML5+CSS3+jQuer简单界面模板示例及源码.rar ProgressBar 几乎全部的用法.rar QQ_UI之分类菜单DEMO.zip QQ的登录界面 源代码 .zip quitesleep手机电话功能软件.rar rokon_src_2-0-3_游戏引擎.zip scientific-calculator-for-android( 功能强大的科学计算器).zip SeeJoPlayer(播放器).7z SipDroid客户端源码.rar sipdroid语音及视频通话.rar tablelogin(登陆界面).rar TankWar坦克大战.zip telecapoland-jamendo-android-6cd07fb(国外开源音乐播放器) TorProxy应用实现了Android手机无线电电传通讯(TOR).rar UI设计之 仿做蘑菇街UI设计 源码.zip ViewPager-实现左右两个屏幕的切换.rar VIEW双缓冲与SurfaceView比较.zip weibo4andriod-2011-01-14.zip WordPress for Android zirco-browser浏览器源码.rar Zirco-browser:超越海豚的开源浏览器.rar zz-doctor中医大夫助

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值