事件驱动编程模型
事件驱动编程是一种编程范式,它的执行流由外部决定。它的特点是包含一个事件循环,当外部事件发生时,使用回调机制来触发相应的处理。
我们先来看看为Windows编程中事件驱动编程模型代码是什么样子的。
main(){
while(1) //消息循环
{
id=getMessage(...); //获取消息、如无消息则阻塞
if(id == quit)
break;
translateMessage(...);//处理消息
}
}
它很简单,就在一个死循环中中去获取和处理消息。与其对立的还有顺序编程模型,还记得我们编写的第一个helloworld吗?它就是顺序编程模型,执行完毕程序即结束。试想一下如果我们的GUI程序(图形界面应用程序)如果是顺序驱动编程模型,顺序执行完语句,执行完程序就结束了。用户还没有看到界面程序就结束了, 很忧伤有没有。事件驱动编程模型很好的解决了这个问题,所以几乎所有的GUI应用,都是采用事件驱动编程,比如Windows、 Linux、Android、iOS等。。
简单说,事件驱动包括事件生产者,事件消费者,消息循环looper。 生产者将消息发送给looper,由looper分发相应的事件消费者处理。
Android 中的事件编程驱动模型
Android中事件驱动主要包含Handler,MessageQueue,Runnable\Message和Looper,他们之间的交互图:
-
Runnable\Message 可以被压入MessageQueue中,MessageQueue中实际只允许存储一种类型对象,而源码中对Runnable进行了相应的转换(转换为Message)。Message中存有Handler的引用(target),在从MessageQueue中取出Message后交给它所引用的Handler.handleMessage()去处理。
-
MessageQueue 存储消息的队列,实际上是一个单链表数据结构。
-
Looper 一个消息循环体,不断从MessageQueue中取出Message然后传给Handler处理,如此往复。如果队列为空,则该Looper所在线程进行休眠。
-
Handler 真正的发送以及处理消息的地方。
一句话概括它们:Looper不断获取MessageQueue中的一个Message,然后交由Handler进行处理。就好比CPU的工作方式,中央处理器(Looper)不断地从内存(MessageQueue)中读取指令(Message),执行指令(Handler)并最终产生结果。
源码分析
接下来通过源码来分析一下Android中的事件驱动编程模型,AndroidUI主线程入口-ActivityThread.main()的源码:
public static void main(String[]args){
...
Looper.prepareMainLooper(); //1
ActivityThread thread = new ActivityThread(); //2
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler(); //3
}
Looper.loop(); //4
throw new RuntimeException("Main thread loop unexpectedly exited");
}
从注释1到注释4依次来看,注释1处初始化looper对象
看注释1处 Looper.prepareMainLooper()的源码:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到prepare()方法此处为对Looper的创建, 并且存入当前线程的ThreadLocal对象中。那么ThreadLocal是什么?
ThreadLocal是一个用来存储数据的类,类似HashMap、ArrayList等集合类。它的特点是可以在指定的线程中存储数据,然后取数据只能取到当前线程的数据。
注释2因为在main静态方法中,所以要创建ActivityThread对象, 然后与WindowManagerService建立联系开启binder线程。
注释3创建主线程Handler对象thread.getHandler(),看源码:
final Handler getHandler() {
return mH;
}
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
}
}
LAUNCH_ACTIVITY、PAUSE_ACTIVITY是不是很熟悉,这就是我们Activity中的生命周期,binder线程像主线程Hander.H对象发送消息,最终调用Activity.onCreate()等生命周期方法。
注释4开启主消息循环,来看源码Looper.loop():
public static void loop() {
final Looper me = myLooper(); //first
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
首先通过myLooper()获取到当前线程的looper对象,然后从looper对象中获取到消息队列me.mQueue。接下来就是我们的消息循环了,不断的从队列中取出消息queue.next();,如果消息队列没有消息就堵塞,等到有消息的时候会被唤醒。通过msg.target.dispatchMessage(msg);来对消息进行处理,target为Message中对handler的引用,Hanlder.dispatchMessage()源码:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
所以消息的执行会调用到我们重写的mCallback.handleMessage()ormessage.callback.run()。
消息入队顺序问题
下面来看一下消息入队的,并抛出一个问题,Handler消息入队是按发送顺序执行的吗?继续看源码:
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.when = when;
Message p = mMessages;
boolean needWake;
// 队列中之前没有消息、或者延迟时间为零或当前消息的延迟时间小于之前消息的延迟时间的话插入到头结点
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//如果不满足头结点要求, 这里会进行遍历,找到延迟时间比当前消息长的消息,并将当前消息插入其前面节点。
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
可以看到实际上消息入队是有一个优先级的条件的。在符合队列先进先出规则的同时增加了按延迟时间进行排序。
例如:postDelay先发送了一个五秒延迟的消息A,再postDelay延迟两秒发送消息B,结果会在2秒后先执行消息A,然后3秒后执行消息B。
handler处理消息所在线程
在handler处理消息所在线程上的理解,重要的一点:
handler消息处理所在线程 = handler所引用的looper所在的线程
练习
最后可以自己实现的handlerThread 这样可以加深对handler消息机制的理解~ 在主线程中向子线程发送消息,然后在子线程的handler中进行处理。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Log.d("MainActivity", "mainThread:"+Thread.currentThread().getName());
MyThread thread = new MyThread();
Handler h = new Handler(thread.getLooper()){
@Override
public void handleMessage(Message msg) {
Log.d("MainActivity", "handlerThread:"+Thread.currentThread().getName());
}
};
thread.start();
h.sendEmptyMessage(0);
}
class MyThread extends Thread{
@Override
public void run() {
Looper.prepare();
Looper.loop();
}
public Looper getLooper(){
return Looper.myLooper();
}
}
}