Android事件驱动编程模型及Handler原理

2018/03/12 Android源码分析

事件驱动编程模型

事件驱动编程是一种编程范式,它的执行流由外部决定。它的特点是包含一个事件循环,当外部事件发生时,使用回调机制来触发相应的处理。

下面为Windows通过消息循环的伪代码,以此可以更好的理解事件驱动的实现。

main(){
    while(1) //消息循环
    {
	id=getMessage(...);  //获取消息、如无消息则阻塞
	if(id == quit)
		break;
	translateMessage(...);//处理消息
    }
}

很容易理解的一段代码,在一个死循环中中去获取和处理消息。

试想一下如果我们的GUI程序如果是顺序驱动编程模型,类似于我们第一个程序helloworld,顺序执行完语句,执行完程序就结束了。用户还没有看到界面程序就结束了, 很忧伤有没有。事件驱动编程模型很好的解决了这个问题,所以几乎所有的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处 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;
    }

可以看到实际上消息入队是有一个优先级的条件的。在符合队列先进先出规则的同时增加了按延迟时间进行排序。这样在使用延迟消息发送上也正是符合我们正常思维的,入两秒后执行的消息就会在两秒后执行, 即使前面已经发送了一个五秒延迟的消息。

延伸

Handler使用场景不光是在子线程切换主线程操作UI上,还有HandlerThread, 它可以帮我们开启一个带有消息循环的子线程,可以作为一个后台的守护的线程,这里来看HandlerThread.run()源码:

@Override
    public void run() {
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
    }

可以看到它只是在run中增加了Looper,接下来来看它的使用。

//创建一个线程,线程名字:handler-thread
myHandlerThread = new HandlerThread( "handler-thread") 
//开启一个线程
myHandlerThread.start();        //在这个线程中创建一个handler对象
handler = new Handler( myHandlerThread.getLooper() ){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
        Log.d( "handler " , "消息: " + msg.what + "  线程: " + Thread.currentThread().getName()  ) ;
        }
    };
    //在主线程给handler发送消息
    handler.sendEmptyMessage( 1 ) ;
    new Thread(new Runnable() {
        @Override
        public void run() {
         //在子线程给handler发送数据
         handler.sendEmptyMessage( 2 ) ;
         }
    }).start() ;

重点看handler = new Handler( myHandlerThread.getLooper() ) 根据HandlerThread对象的looper创建了Handler,消息是在Looper中被处理执行的,而消息执行所在的线程也正是Looper所在的线程。

IntentService

在使用HandlerThread过程中会受生命周期影响优先级较低在内存不足情况容易被杀死。这时候可以使用IntentService,它继承自普通 Service 同时又在内部创建了一个HandlerThread,在onHandlerIntent()的回调里面处理扔到 IntentService 的任务,在执行完任务后会自动停止。所以IntentService 就不仅仅具备了异步线程的特性,还同时保留了 Service 不受主页面生命周期影响,优先级比较高,适合执行高优先级的后台任务,不容易被杀死的特点。来看IntentService.onCreate() 和 ServiceHandler类源码:

@Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

可以看到onCreate方法中穿件了Handlerthread 以及ServiceHandler对象。而ServiceHandler中handleMessage中执行了onHandleIntent();onHandleIntent正是我们使用IntentService需要重写的方法。那么消息是什么时候发送的呢?

 @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

正是在onStart方法中发送的消息。很好理解它只是在Service中封装了HandlerThread+Handler。这样可以让我们在一个服务中启动线程,来处理相关的逻辑。

Search

    Table of Contents