Android消息机制

概述

作用

为什么不能在主线程中进行耗时操作?

因为会导致体验不流畅、卡顿,体验差。

Handler的出现为了解决在子线程中操作完成了无法访问UI线程(主线程)的矛盾。

为什么不能在子线程中更新UI?

因为多线程操作导致线程不同步,并发操作让UI控件处于不可预期的状态。

为什么不通过线程同步更新UI?通过给UI控件的访问加上锁

1. 加锁会让UI访问逻辑变得异常复杂

2. 锁阻塞某些线程的执行,降低UI访问效率

ThreadLocal

为什么要使用ThreadLocal

ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度.

ThreadLocal是一个线程内部数据存储类,当某些数据是以线程为作用域,并且在不同线程是具有不同的数据副本的时候,就可以考虑使用它。例如:在一个复杂的使用场景,比如线程内,一个监听器对象需要贯穿整个线程的执行,在不使用ThreadLocal的情况下解决思路:

1. 把监听器对象 作为函数的一个参数,在函数之间传递。如果当函数调用栈很深的时候,这样的设计看起来会特别糟糕。

2. 把监听器作为一个静态变量。在单一线程中执行,这是没有问题的,但是如果是多个线程呢,那么我们需要为不同线程创建一个不同的监听器,如果是10个线程就是10个静态监听器,显然这也不是一个良好的设计。

在Handler的使用,需要获得当前线程的Looper,很显然Looper的作用域是当前线程,并且不同的线程具备不同的Looper,使用Handler就可以轻松读取。

怎么使用ThreadLocal

定义一个储存Integer类型的ThreadLocal对象,在分别找主线程,子线程1,子线程2中修改和访问它的值

    private ThreadLocal<Integer> mThreadLocal = new ThreadLocal<Integer>();

    private void testThreadLocal() {
        ExecutorService ePool = Executors.newFixedThreadPool(2);
        mThreadLocal.set(0);
        Log.d("TAG", "ThreadMain:" + mThreadLocal.get());
        ePool.execute(new Thread("Thread1") {
            @Override
            public void run() {
                super.run();
                mThreadLocal.set(1);
                Log.d("TAG", "Thread1:" + mThreadLocal.get());
            }
        });

        ePool.execute(new Thread("Thread2") {
            @Override
            public void run() {
                super.run();
                Log.d("TAG", "Thread2:" + mThreadLocal.get());
            }
        });
    }

结果:

05-31 16:33:30.698: D/TAG(11113): ThreadMain:0
05-31 16:33:30.698: D/TAG(11113): Thread2:null
05-31 16:33:30.698: D/TAG(11113): Thread1:1

因为子线程2中没有设置过mThreadLocal的值,所以返回了一个空对象。可以看到在不同线程中,获得的mThreadLocal对象值是不一样的。

怎么实现ThreadLocal

如果让我们自己来实现如何实现?

在Thread中创建一个Map命名为threadLocals,利用当前运行的Thread作为Key,存储对象Object为Value,在不考虑null处理、泛型、初始化的时候实现方式。

public class ThreadLocal {
    private Map getMap() {
        return Thread.currentThread().threadLocals;
    }
    public Object get() {
        return getMap().get(this);
    }
    public void set(Object value) {
        getMap.put(this, value);
    }
}

源码实现方式

set( )方法

    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

initialValue()初始化方法

    protected T initialValue() {
        return null;
    }

get()方法

    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

发现其实系统实现的方式和我们想的差不多,只是Values这个类扮演了我们自己实现方式的threadLocals用来具体存储数据。

  if (values == null) {
            values = initializeValues(currentThread);
        }

如果存储具体对象values没有初值,就使用初始化方法initializeValues()实例化它。例如将ThreadLocal使用的例子初始化改为

    private ThreadLocal<Integer> mThreadLocal = new ThreadLocal<Integer>() {

        @Override
        protected Integer initialValue() {
            return 100;
        }
    };

Thread2调用

        ePool.execute(new Thread("Thread2") {
            @Override
            public void run() {
                super.run();
                Log.d("TAG", "Thread2:" + mThreadLocal.get());
            }
        });

因为ThreadLocal没有初始化,直接调用initializeValues()方法得到:

05-31 16:33:30.698: D/TAG(11113): Thread2:100

Looper

使用场景

在消息机制里,每个Thread都需要有一个独立的Looper对象,通过ThreadLocal实现。现在来看看一个Looper结合Handler的典型使用场景:

    class LooperThread extends Thread {
        public Handler mHandler;

        public void run() {
            Looper.prepare();

            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
            Looper.loop();
        }
    }

一个具备消息机制的线程实现,大致需要3个步骤:

  1. Looper.prepare(); Looper准备
  2. new Handler,同时处理消息
  3. Looper.loop();Looper循环

    查看源码他们的实现:

Looper.prepare()

    public static void prepare() {
        prepare(true);
    }

    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));
    }

prepare(boolean quitAllowed)方法调用的开始会判断sThreadLocal.get() != null,这说明prepare()方法在同一个线程中不能调用多次, 也不能重写ThreadLocal的initializeValues()方法,创建默认Looper。然后把Looper设置到sThreadLocal,根据sThreadLocal的特性可以保证他在独立线程中的唯一性。

Looper.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.");
        }
        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();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);
            ...省略部分代码
            msg.recycle();
        }
    }

    public static Looper myLooper() {
        return sThreadLocal.get();
    }
  1. 先去通过myLooper()获取到Looper,判断是否为空,如果为空证明Looper.prepare()没有调用,抛出异常。
  2. for (;;) {}实现一个死循环,让Looper循环执行。
  3. me.mQueue拿到消息队列MessageQueue,然后通过queue.next()方法取出消息,这是个阻塞方法,如果消息消息队列就阻塞在这里,等待消息。
  4. 如果拿到消息就通过msg.target.dispatchMessage(msg);处理消息,这就回到了当前线程,这里的msg.target其实就是当前线程的Handler
  5. msg.recycle()回收消息

MessageQueue

next()

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

            // We can assume mPtr != 0 because the loop is obviously still
            // running.
            // The looper will not call this method after the loop quits.
            nativePollOnce(mPtr, 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 (false)
                            Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have
                // been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first
                // message
                // in the queue (possibly a barrier) is due to be handled in the
                // future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run. Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(
                            pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers
                        .toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the
                                                // handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been
            // delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
  1. 在Looper里面提到Looper.loop()中通过MessageQueue的next()方法获取Message和实现阻塞。
  2. MessageQueue的next方法中,也是通过无限循环实现获取消息,private native void nativePollOnce(mPtr, nextPollTimeoutMillis);通过这个native方法,实现消息获取。当没有消息时会令nextPollTimeoutMillis为-1,调用方法么,实现了阻塞。
  3. MessageQueue虽然取名为Queue但是实际上,并不是Queue(队列)而是通过单链表来实现。这一点在插入消息的方法enqueueMessage()中可以更加清晰的看出

enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        synchronized (this) {
            if (mQuitting) {
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }

            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 {
                // Inserted within the middle of the queue.  Usually we don‘t have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

Handler

send()、post()

Handler通过sendMessageAtTime()来发送消息,他具体是根据MessageQueue的enqueueMessage()方法来实现。

    public boolean sendMessageAtTime(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);
    }

封装实现sendMessageDelayed()

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

postDelayed() post() postAtTime()一系列方法也是通过sendMessage()方法封装实现

    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

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);
        }
    }

执行步骤:

1. 先判断msg的callback是否为空,不为空就直接调用他的handleCallback()方法。

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

Message的callback方法可以通过他的obtain()方法设置

    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

2.如果 msg.callback == null ,判断mCallback是否为空,不为空的话,执行mCallback.handleMessage(msg)

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

看到mCallback其实是接口Callback的实例,这个接口的实现,也就是使用中最常用的用法

        Handler handler = new Handler(new Callback() {

            @Override
            public boolean handleMessage(Message msg) {
                // TODO Auto-generated method stub
                return false;
            }
        });

应该很熟悉吧,这是一种巧妙的实现方式,设想如果没有这个接口实现的方式。那么就要通过派生Handler的一个子类,然后重写他的handleMessage()方法,而把Callback作为构造函数的一个参数传递给匿名内部类,省去了很多麻烦。

3.最后执行handleMessage(msg)

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

Callback 接口中的同名函数handleMessage()

执行流程:

Created with Rapha?l 2.1.0Startmsg.callback == null mCallback != nullhandleMessage(msg)EndhandleMessage(msg)handleCallbackyesnoyesnoyesno

主线程的消息循环

Android的主线程就是ActivityThread,主线程的入口函数和Java程序的入口函数一样是public static void main(String[] args)


    public static void main(String[] args) {
         //......省略部分代码

        Looper.prepareMainLooper();  

        // 创建ActivityThread实例
        ActivityThread thread = new ActivityThread();
        thread.attach(false);  

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

        AsyncTask.init();  

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

        Looper.loop();  

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

同样,主线程的Looper Handler 和MessageQueue开启流程和在Thead中开启大体一致,使用的是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();
        }
    }

发现prepareMainLooper()其实就是调用的prepare(false);实现,只是多了一个主线程中的Looper初始化赋值的步骤sMainLooper = myLooper()

当然在Looper中也存在退出的操作

  public void quit() {
        mQueue.quit(false);
    }
    public void quitSafely() {
        mQueue.quit(true);
    }

这里quit()是直接退出,quitSafely()等待队列任务执行完成后退出

时间: 2024-08-01 07:07:07

Android消息机制的相关文章

【转】Android 消息机制

Android 消息机制 本文首先从整体架构分析了Android整个线程间消息传递机制,然后从源码角度介绍了各个组件的作用和完成的任务.文中并未对基础概念进行介绍,关于threadLacal和垃圾回收等等机制请自行研究. 基础架构 首先,我们需要从整体架构上了解一下Android线程通信都做了哪些工作.我们都知道,进程是操作系统分配资源的最小单位,一个进程中可以启动多个线程来执行任务,这些线程可以共享进程的资源但不分配资源,这里讲的资源主要是只内存资源.Android的线程间消息传递机制其实和我

Android消息机制字典型探

Android消息机制字典型探究(一) Android消息机制字典型探究(二) 带着这篇去通关所有Handler的提问(三)

Android消息机制:Looper,MessageQueue,Message与handler

Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Message是什么? 看一个从消息池中取出一个msg的方法: public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) { Message m = obtain(); m.target = h; m

Android消息机制探索(Handler,Looper,Message,MessageQueue)

概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程中想更新UI,就通过发送更新消息到UI线程中来实现. 2.任务延迟执行.比如30秒后执行刷新任务等. 消息机制运行的大概示意图如下: 一个线程中只能有一个Looper对象,一个Looper对象中持有一个消息队列,一个消息队列中维护多个消息对象,用一个Looper可以创建多个Handler对象,Han

Android消息机制1-Handler(Java层)(转)

转自:http://gityuan.com/2015/12/26/handler-message-framework/ 相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.java framework/base/core/java/andorid/os/Message.java framework/base/core/java/andorid/os/Mes

Android消息机制Handler的实现原理解析

Android的主线程为什么可以一直存在? 线程是一个动态执行的过程,从产生到死亡包括五个状态:新建.就绪.运行.死亡和堵塞.只要线程没有执行完毕或者没有被其它线程杀死,线程就不会进入死亡状态.Android中的主线程一直存在是因为主线程中一直在监听消息,从而使线程无法被执行完毕. 线程的五种状态: 新建new Thread 当创建Thread类的一个实例对象时,此线程进入新建状态未被启动. 就绪runnable 线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等

Android 消息机制 (Handler、Message、Looper)

综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidnews/201204/421642.shtml 一. 消息机制常用类的介绍和使用 在Android程序运行中,线程之间或者线程内部进行信息交互时经常会使用到消息,如果我们熟悉这些基础的东西及其内部的原理,将会使我们的Android开发变的容易.可以更好地架构系统.在学习Android消息机制之前,我们

Android消息机制Handler、Looper、MessageQueue源码分析

1. Handler Looper MessageQueue的关系 2.源码分析 下图表示了Handler.Looper.MessageQueue.Message这四个类之间的关系. Handler必须与一个Looper关联,相关Looper决定了该Handler会向哪个MessageQueue发送Message 每一个Looper中都包含一个MessageQueue Handler中的mQueue引用的就是与之关联的Looper的MessageQueue 不管Handler在哪个线程发送Mes

android消息机制原理详解

android消息机制原理详解 因为之前使用的是CSDN默认的文本编辑器,而且也因为懒得学用MarkDown来写博客,所以排版上有一些问题.就上一篇写的设计模式之抽象工厂模式提出了这个问题(一个android群的群友提出来的,没有在评论里评论),所以以后的文章都用MarkDown来写了. 好了,言归正传,这篇文章我来介绍一下android消息机制的原理 Android消息机制概述 说到Android的消息机制,Android初级工程师(不包括那些初学者)肯定会想到Handler.是的,Andro

网易视频云技术分享:Android 消息机制学习

Android消息机制大家都不陌生,想必大家也都看过Handler.Looper的源码(看过可以直接看末尾重点,一款监控APP卡顿情况的控件),下面,网易视频云技术专家就整合一下这方面的资料,加深对这方面的印象. 用法 private Handler mHandler = new Handler() {    @Override public void handleMessage(Message msg) {        switch (msg.what) {            case