美文网首页
(十)Android的消息机制

(十)Android的消息机制

作者: YongtaoHuang | 来源:发表于2019-08-28 23:27 被阅读0次

Handler时Android消息机制的上层接口,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。
Android的消息机制指的是Handler的运行机制。
本质上说,Handler不是专门用来更新UI的,它只是常被开发者用来更新UI。
Handler的运行需要底层的MessageQueue和Looper的支撑。
※ Handler:负责Message的发送及处理。
1、Handler.sendMessage():向消息池发送各种消息事件。
2、Handler.handleMessage() :处理相应的消息事件。
※ Message:需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
※ MessageQueue:用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。
※ Looper:通过Looper.loop()无限循环地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。

ActivityThread,主线程,被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因,详见如下源码: Looper.prepareMainLooper();

public final class ActivityThread {
    public static final String TAG = "ActivityThread";
    final Looper mLooper = Looper.myLooper();
    public static void main(String[] args) {
        
        // ......

        Looper.prepareMainLooper();


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

        Looper.loop();
        
        // ......

    }
}

10.1 Android的消息机制概述

开发过程我们中比较多地接触Handler,Handler的主要作用是将一个任务切换到某一个指定的线程。
Android不允许在子线程中更新UI,所以需要Handler。

    private TextView tvText;
    private Button btnChange;

    private static final int UPDATE_TEXT = 1;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // 主线程处理
            switch (msg.what){
                case UPDATE_TEXT:
                    tvText.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvText = findViewById(R.id.tv_text);
        btnChange = findViewById(R.id.btn_change);
        btnChange.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_change:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 子线程发送Message
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();
                break;
            default:
                break;
        }
    }

代码分析:点击事件开启了Thread,创建了Message,介者调用handler.sendMessage(message),然后在handleMessage()方法中处理,显然handleMessage()是在主线程中执行。


handler.png

10.2 Android的消息机制分析

10.2.1 ThreadLocal的工作原理

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取存储的数据,对于其他线程来说则无法获取到数据。

    private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<Boolean>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBooleanThreadLocal.set(true);
        Log.d("MainActivity", "onCreate: "+"[Thread#main]"+mBooleanThreadLocal.get());

        new Thread("Thread#1"){
            @Override
            public void run() {
                mBooleanThreadLocal.set(false);
                Log.d("MainActivity", "onCreate: "+"[Thread#1]"+mBooleanThreadLocal.get());
            }
        }.start();

        new Thread("Thread#2"){
            @Override
            public void run() {
                Log.d("MainActivity", "onCreate: "+"[Thread#2]"+mBooleanThreadLocal.get());
            }
        }.start();

    }

输出结果:

2019-08-29 13:58:23.649 5983-5983/? D/MainActivity: onCreate: [Thread#main]true
2019-08-29 13:58:23.652 5983-6008/? D/MainActivity: onCreate: [Thread#1]false
2019-08-29 13:58:23.652 5983-6009/? D/MainActivity: onCreate: [Thread#2]null

从日志可以看出,虽然在不通线程中访问的是同一个ThreadLocal对象,但是它们通过ThreadLocal获取到的值却是不一样。我们来阅读ThreadLocal.java中的set()和get()方法。

    public void set(T value) {
        // 获取当前的线程
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        // 往map里面set值
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    public T get() {
        // 获取当前的线程
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        // 获取map里面的值
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

总结:ThreadLocal可以在多个线程中互不干扰地存储和修改数据。

10.2.2 消息队列的工作原理

消息队列是指Android中的MessageQueue,主要包含
1、插入:enqueueMessage()
2、读取:next()
两个方法。事实上她是通过单链表数据结构来维护消息列表,enqueueMessage主要操作就是单链表的插入操作。

boolean enqueueMessage(Message msg, long when) {
    ...
    synchronized (this) {
        ...
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            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;
        }
        ...
    }
    return true;
}

next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法将一直阻塞在这里。

Message next() {
    
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            ...
        }
        ... 
    }
}

10.2.3 Looper的工作原理

Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则就一直阻塞在那里。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    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) { // 唯一的跳出循环的条件,MessageQueue.next() == null
            // No message indicates that the message queue is quitting.
            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);
        }

        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;
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            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();
    }
}

10.2.4 Handler的工作原理

Handler的工作主要包含消息的发送和接收过程:
1、发送:post()系列和send()系列,核心方法sendMessage(Message msg)
2、接收:dispatchMessage(Message msg)

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

10.3 主线程的消息循环

Android的主线程就是ActivityThread。
核心代码两行:
Looper.prepareMainLooper(); // 创建主线程的Looper以及MessageQueue;
Looper.loop(); // 开启主线程的消息循环。

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

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new AndroidKeyStoreProvider());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper(); // 创建主线程的Looper以及MessageQueue

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

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

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

        Looper.loop(); // 开启主线程的消息循环

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

ActivityThread内部定义了H,用来和消息队列交互。

    private class H extends Handler {
        // 开启活动
        public static final int LAUNCH_ACTIVITY         = 100;
        // 暂定活动
        public static final int PAUSE_ACTIVITY          = 101;
        // ...省略

        String codeToString(int code) {
            if (DEBUG_MESSAGES) {
                switch (code) {
                    case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
                    case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
                    // ...省略
                }
            }
            return Integer.toString(code);
        }
    }

ApplicationThread向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程去执行,这个过程就是主线程的消息循环模型。

相关文章

网友评论

      本文标题:(十)Android的消息机制

      本文链接:https://www.haomeiwen.com/subject/dapqectx.html