• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

AndroidAndroid的消息机制

android 搞代码 3年前 (2022-03-01) 17次浏览 已收录 0个评论
文章目录[隐藏]

「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()
<code class="java">
    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()办法:

<code class="java">    /**
     * 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()办法:

<code class="java">    /**
     * 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中查看是否有新音讯,如果有新音讯就会立即解决,否则始终阻塞。

构造方法:

<code class="java">    private Looper(boolean quitAllowed) {
        // 创立一个MessageQueue音讯队列
        mQueue = new MessageQueue(quitAllowed);
        // 保留以后线程的对象
        mThread = Thread.currentThread();
    }

创立Looper:

<code class="java">        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():

<code class="java">
    /**
     * 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

<code class="java">
    /**
     * 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():

<code class="java">    
    /**
     * 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():

<code class="java">    private static void handleCallback(Message message) {
        message.callback.run();
    }

其次,查看mCallback是否为null,不为null就调用mCallback的handleMessage办法来解决音讯。其中Callback是一个接口,其定义如下:

<code class="java">
    /**
     * 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对象:

<code class="java">Handler handler = new Handler(callback);

能够用来创立一个不派生Handler子类的Handler的对象。在日常开发中,创立Handler最常见的形式就是派生一个Handler的子类并重写其handleMessage办法来解决具体的音讯,而Callback给咱们提供了另外一种应用Handler的形式。

最初,调用Handler的handleMessage办法来解决音讯。

主线程的音讯循环

Android的主线程是ActivityThread,入口为main函数。在main函数中零碎会通过Looper.prepareMainLooper()来创立主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的音讯循环。

<code class="java">    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类的对象,它外部定义了一组音讯类型,次要蕴含四大组件的启动、进行等过程:

<code class="java">
    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中执行,也即切换回主线程执行。这个过程,就是主线程的音讯循环模型。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:AndroidAndroid的消息机制

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址