「Android」Android的消息机制

概述

Android的消息机制主要指的是Handler机制。Handler的运行需要MessageQueueLooper的支撑:

  • MessageQueue:消息队列。内部存储了一组信息,以队列的形式对外提供插入和删除的工作,内部存储结构以单链表的数据结构来存储消息列表。
  • Looper:消息循环。MessageQueue只是一个消息的存储单元,不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待。

Looper中还有一个特殊概念就是ThreadLocal,ThreadLocal不是线程,它的作用是在每个线程中互不干扰的存储并提供数据。Handler创建的时候会采用当前线程的Looper来构造消息循环系统,而此处获取当前线程Looper的方式就是通过ThreadLocal。

线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper。主线程,也即UI线程ActivityThread,被创建时会初始化Looper,所以主线程默认可以直接使用Handler。

Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程,实际上是一个整体。Handler的主要作用是将一个任务切换到某个指定的线程中去执行

  1. 为什么Android不允许子线程访问UI?
    答:因为Android的UI控件不是线程安全的,如果在多线程中并发访问会导致UI控件处于不可预期的状态。
  2. 为什么不对UI控件的访问加锁?
    答:首先,加锁会让UI访问逻辑变得复杂;其次,加锁会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。

使用

  • sendEmptyMessage()
  • sendMessage()
  • post()

    Handler mHanlder = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    Log.i(TAG, "thread 1");
                case 1:
                    Log.i(TAG, "thread 2");
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        new Thread("Thread#1") {
            @Override
            public void run() {
                super.run();
                // sendEmptyMessage
                mHanlder.sendEmptyMessage(0);
            }
        }.start();

        new Thread("Thread#2") {
            @Override
            public void run() {
                super.run();
                Message message = Message.obtain();
                message.what = 1;
                message.obj = "obj";
                // sendMessage
                mHanlder.sendMessage(message);
            }
        }.start();

        new Thread("Thread#3") {
            @Override
            public void run() {
                super.run();
                // post
                mHanlder.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.i(TAG, "thread 3");
                    }
                });
            }
        }.start();
    }

可以通过Handler的send方法发送一个消息,消息会在Looper中处理;可以通过Hander的post方法将一个Runnable投递到Looper中处理,其实post也是通过send方法来完成的。

Handler的send方法被调用时,会调用MessageQueue的enqueueMessage方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,就会处理这个消息。最终消息中的Runnable或者Handler的handleMessage方法就会被调用。Looper是运行在创建Handler所在的线程中的,这样一来Handler的业务逻辑就被切换到了创建Handler所在的线程中去执行了。

ThreadLocal

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说无法获取该数据。当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,可以考虑采用ThreadLocal。
比如,对于Handler来说,需要获取到当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候可以通过ThreadLocal轻松获取。

不同线程中访问同一个ThreadLocal对象的set或者get方法,它们对ThreadLocal对象所做的读写操作仅限于各自线程的内部。所以ThreadLocal可以在多个线程中互不干扰的存储和修改数据。

ThreadLocal的set()方法:

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

不同线程访问同一个ThreadLocal对象的set()方法时,ThreadLocal会从各自线程Thread t中取出一个ThreadLocalMap对象,然后操作该线程的ThreadLocalMap对象。

ThreadLocal的get()方法:

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

不同线程访问同一个ThreadLocal对象的get()方法时,ThreadLocal会从各自线程Thread t中取出一个ThreadLocalMap对象,然后操作该线程的ThreadLocalMap对象。

MessageQueue

MessageQueue主要包含两个操作:

  • 插入 = enqueueMessage:往消息队列中插入一条消息
  • 读取+删除 = next:从消息队列中取出一条消息并将其从队列中移除

Looper

Looper主要扮演着消息循环的角色,会不停的从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则一直阻塞。

构造方法:

    private Looper(boolean quitAllowed) {
        // 创建一个MessageQueue消息队列
        mQueue = new MessageQueue(quitAllowed);
        // 保存当前线程的对象
        mThread = Thread.currentThread();
    }

创建Looper:

        new Thread("Thread#4") {
            @Override
            public void run() {
                super.run();
                // 为当前线程创建Looper
                Looper.prepare();
                Handler handler = new Handler();
                // 开启消息循环
                Looper.loop();
            }
        }.start();

由于主线程的Looper比较特殊,所以Looper提供一个getMainLooper方法,通过它可以在任何地方获取到主线程的Looper。

Looper是可以退出的,Looper提供了quit和quitSafely来退出一个Looper,二者的区别是:quit会直接退出Looper,而quitSafely只是设定一个退出标记然后等消息队列中的已有消息处理完毕后才安全的退出。在子线程中,如果手动为其创建Looper,那么在事情完成后二需要调用quit方法来终止消息循环,否则这个子线程就会一直处于等待状态,如果退出Looper以后,线程会立刻终止。

Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。

loop():


    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        for (;;) {
            // 阻塞操作
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                // 退出状态时才会返回null
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。而当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。也就是说,Looper必须退出,否则loop方法会无限循环下去。

loop方法调用MessageQueue的next方法来获取新消息,next方法是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,也导致loop方法一直阻塞。

当next方法返回新的消息,Looper就会处理这个消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又会交给Handler的dispatchMessage方法来处理。Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功将代码逻辑切换到指定线程中执行了。

Handler


    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler发送消息的过程仅仅是向消息队列插入一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理,最终消息由Looper交由Handler的dispatchMessage方法处理,此时Handler进入消息处理阶段。

消息处理

dispatchMessage():

    
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

首先检查Message的callback是否为null,不为null就通过handleCallback来处理消息,Message的callback是一个Runnable对象,实际上就是Handler的post方法传递的Runnable参数。

handleCallback():

    private static void handleCallback(Message message) {
        message.callback.run();
    }

其次,检查mCallback是否为null,不为null就调用mCallback的handleMessage方法来处理消息。其中Callback是一个接口,其定义如下:


    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

通过Callback可以采用如下方式创建Handler对象:

Handler handler = new Handler(callback);

可以用来创建一个不派生Handler子类的Handler的对象。在日常开发中,创建Handler最常见的方式就是派生一个Handler的子类并重写其handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种使用Handler的方式。

最后,调用Handler的handleMessage方法来处理消息。

主线程的消息循环

Android的主线程是ActivityThread,入口为main函数。在main函数中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。

    public static void main(String[] args) {
        ......

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

主线程的消息循环开始之后,ActivityThread需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H类的对象,它内部定义了一组消息类型,主要包含四大组件的启动、停止等过程:


    class H extends Handler {
        public static final int BIND_APPLICATION        = 110;
        @UnsupportedAppUsage
        public static final int EXIT_APPLICATION        = 111;
        @UnsupportedAppUsage
        public static final int RECEIVER                = 113;
        @UnsupportedAppUsage
        public static final int CREATE_SERVICE          = 114;
        @UnsupportedAppUsage
        public static final int SERVICE_ARGS            = 115;
        @UnsupportedAppUsage
        public static final int STOP_SERVICE            = 116;

        public static final int CONFIGURATION_CHANGED   = 118;
        public static final int CLEAN_UP_CONTEXT        = 119;
        @UnsupportedAppUsage
        public static final int GC_WHEN_IDLE            = 120;
        @UnsupportedAppUsage
        public static final int BIND_SERVICE            = 121;
        @UnsupportedAppUsage
        public static final int UNBIND_SERVICE          = 122;
        public static final int DUMP_SERVICE            = 123;
        public static final int LOW_MEMORY              = 124;
        public static final int PROFILER_CONTROL        = 127;
        public static final int CREATE_BACKUP_AGENT     = 128;
        public static final int DESTROY_BACKUP_AGENT    = 129;
        public static final int SUICIDE                 = 130;
        @UnsupportedAppUsage
        public static final int REMOVE_PROVIDER         = 131;
        public static final int DISPATCH_PACKAGE_BROADCAST = 133;
        @UnsupportedAppUsage
        public static final int SCHEDULE_CRASH          = 134;
        public static final int DUMP_HEAP               = 135;
        public static final int DUMP_ACTIVITY           = 136;
        public static final int SLEEPING                = 137;
        public static final int SET_CORE_SETTINGS       = 138;
        public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
        @UnsupportedAppUsage
        public static final int DUMP_PROVIDER           = 141;
        public static final int UNSTABLE_PROVIDER_DIED  = 142;
        public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
        public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
        @UnsupportedAppUsage
        public static final int INSTALL_PROVIDER        = 145;
        public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
        @UnsupportedAppUsage
        public static final int ENTER_ANIMATION_COMPLETE = 149;
        public static final int START_BINDER_TRACKING = 150;
        public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
        public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
        public static final int ATTACH_AGENT = 155;
        public static final int APPLICATION_INFO_CHANGED = 156;
        public static final int RUN_ISOLATED_ENTRY_POINT = 158;
        public static final int EXECUTE_TRANSACTION = 159;
        public static final int RELAUNCH_ACTIVITY = 160;
        public static final int PURGE_RESOURCES = 161;
        public static final int ATTACH_STARTUP_AGENTS = 162;
        ......
    }

ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式来完成ActivityThread的请求后,回调ApplicationThread中的Binder方法,然后ApplicationThread会向ActivityThread.H发送消息,ActivityThread.H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中执行,也即切换回主线程执行。这个过程,就是主线程的消息循环模型。


山庄的铁匠
15 声望11 粉丝