概述
目前我们了解到在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有Content Provider和Broadcast Receiver)都会跑在这个Process。
一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。
在这么多Thread当中,有一个Thread,我们称之为UI Thread。UI Thread在Android程序运行的时候就被创建,是一个Process当中的主线程Main Thread,主要是负责控制UI界面的显示、更新和控件交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一 个线程中运行。因此,我们认为,UI Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行, 以免阻塞主线程。
那么,UI Thread如何和其他Thread一起工作呢?常用方法是:
诞生一个主线程的Handler物件,当做Listener去让子线程能将讯息Push到主线程的Message Quene里,以便触发主线程的handlerMessage()函数,让主线程知道子线程的状态,并在主线程更新UI。
框架实例学习
UI线程和Lua线程指的是什么
目前我们框架中存在两个线程:UI线程和Lua线程。
每当客户端获得一断新的报文后,都会解析这段报文,解析的动作包括构建DOM树、创建控件、存储并装配样式和属性、执行Lua脚本等。这么多的工作不可能全部放到UI线程中,因此需要有另外一个线程来做这些事情。
我们把这“另外一个线程”称为Lua线程,因为这个线程中进行的动作都是由Lua脚本主动触发的。
和Lua线程对应的,系统自带的主线程即UI线程。
UI线程和Lua线程所负责的工作
- UI线程本身只做和页面刷新有关的工作,如addView、设置控件的LayoutParams、setContentView等。
- Lua线程本身做一些后台的工作,如上文提到的解析报文和样式、执行Lua脚本等工作。其中Lua线程管理了一个任务队列,并且管理控件的属性库和样式库资源。控件的属性库/样式库被Lua线程上任务队列中的任务所使用。
两条线程之间如何进行交互
- UI->Lua:如果需要在UI线程中操作Lua线程所持有的控件属性库/样式库,则需要生成一个任务,并将这个任务放到Lua线程的任务队列上,等待Lua线程从任务队列上拿出并执行。
- Lua->UI:非UI线程的动作想要操作UI线程上的控件,需要使用handler机制,发送message,这是一个Android系统的机制。
代码实例
首先在程序启动时会:
// 构建基础模块和底层类
mAndroidEMPBuilder = onCreateAndroidEMPBuilder();
mEMPRender = new EMPRender(mAndroidEMPBuilder);
之后会在Render中初始化任务环境:
// 构建任务环境
mEMPTaskManager = new EMPTaskManager();
mEMPTaskManager.setEMPRender(this);
实例化EMPTaskManager()任务管理类:
public EMPTaskManager() {
mQueueTask = new QueueTask();//初始化一个任务队列
new MainThread().start();//框架主线程Lua线程启动
mEMPThreadPool = new EMPThreadPool();//初始化一个线程池
}
初始化的任务队列:
class QueueTask {
public boolean mQuiting = false;
private Queue<InstructTask<?, ?>> mQueue = null;
//队列中存放(抽象类)InstructTask<E,F>实例
protected QueueTask() {
mQuiting = false;
mQueue = new LinkedList<InstructTask<?, ?>>();//链式列表,插入和删除性能较高,Arraylist线性列表,读取查找性能较高。
}
protected void put(InstructTask<?, ?> task) {
if (task == null) {
return;
}
synchronized (this) {
if (mQuiting) {
return;
}
mQueue.add(task);
notifyAll();
}
}
protected InstructTask<?, ?> next() {
synchronized (this) {
if (mQueue.isEmpty()) {
return null;
}
return mQueue.poll();
}
}
protected void clear() {
synchronized (this) {
mQueue.clear();
}
}
protected final void quit() {
synchronized (this) {
if (mQuiting) {
return;
}
mQuiting = true;
notifyAll();
}
}
}
(抽象类)InstructTask(E,F),本次任务的输出,作为下次任务的输入,这种模式下,可以把一系列的任务顺序执行。
/**
* 任务执行
*
* @param <E>
* 入口数据对象
* @param <F>
* 出口数据对象
*/
public abstract class InstructTask<E, F> implements Runnable {
private E mE = null;
private InstructTask<F, ?> mNextTask = null;
public InstructTask(E e) {
mE = e;
}
private void setE(E e) {
mE = e;
}
/**
* 添加下一任务
*
* @param nextTask
*/
public final void addNextTask(InstructTask<F, ?> nextTask) {
mNextTask = nextTask;
}
@Override
public final void run() {
F f = doRun(mE);//执行本次次任务,并把结果作为下个任务的入口数据
if (mNextTask != null) {
if (f != null) {
mNextTask.setE(f);
}
mNextTask.run();
}
doFinish();//如果没有next任务,则走finsh结束任务
}
/**
* 任务处理
*
* @param e
* @return
*/
public abstract F doRun(E e);
/**
* 当前任务完成
*
*/
public void doFinish() {
}
}
框架主线程Lua线程,构造一个消息循环机制,MaintThread开启后,一直等待mQueueTask任务队列中poll出一个任务,然后执行,如果没有任务就一直等。
class MainThread extends Thread {
@Override
public void run() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
try {
for (;;) {
if (mQueueTask.mQuiting) {
mQueueTask.clear();
mQueueTask = null;
return;
}
InstructTask<?, ?> task = mQueueTask.next();
if (task == null) {
synchronized (mQueueTask) {
try {
mQueueTask.wait();
} catch (InterruptedException e) {
Utils.printException(e);
}
}
if (task == null) {
continue;
}
}
task.run();
}
} catch (Exception e) {
Utils.printException(e);
} finally {
if (mQueueTask != null && !mQueueTask.mQuiting) {
new MainThread().start();
}
}
}
}
具体在框架中应用,任务链:
- 当程序启动时首先创建一个页面加载任务,下面涉及到的任务全是InstructTask抽象类的实例
InstructTask<String, String> loadTask = mResources.createLoadTask(path, isAddHistory);
//读取报文,并把报文保存在Stack页面保存栈里面
InstructTask<String, ?> parserTask = getParserTask(null, animationType, listener, sltParam);//创建解析任务
loadTask.addNextTask(parserTask);//把解析任务作为加载报文任务的下一个任务mQueueTask.put(loadTask);//把任务链的第一个任务加到任务队列里
- 解析任务中所包含的任务:
// 第一步,slt解析
SLTParserTask sltParserTask = new SLTParserTask(mEMPRender, content, sltParams);
// 第二步,xml解析
ParserTask parserTask = new ParserTask(mEMPRender, content);
//slt解析任务之后执行xml解析任务
sltParserTask.addNextTask(parserTask);
// 第三步,布局
Compose composer = new Compose(mEMPRender);
//把布局任务放到解析任务之后
parserTask.addNextTask(composer);
// 第四步,执行lua
composer.addNextTask(getExecuteTask(null));
/**
* 创建任务执行lua脚本
*
* @param script
* @return
*/
public InstructTask<Object, String> getExecuteTask(Object script) {
return new InstructTask<Object, String>(script) {
@Override
public String doRun(Object e) {
return mEMPLua.loadBuffer(e);
}
};
}