Android中的消息机制

Android中的消息机制

使用handler可以在子线程中发送消息,通知主线程更新UI,但是这只是Handler的一种特殊的使用场景。本质上Handler是可以实现多线程之间的通讯的。
使用Handler消息机制底层是需要用到MessageQueue和Looper的支撑的:Handler发送消息,处理消息,MessageQueue存放消息,Looper循环获取消息并交给Handler处理。他们之间是如何协作呢,我们来分析下。

Looper

Looper作为消息获取者,他的任务便是循环的从MessageQueue中获取消息,如果没有消息便会阻塞,直到有新的消息。Handler的创建和使用是需要Looper的,而普通的线程是不存在Looper的,而我们在使用Handler的时候基本都是Handler handler = new Handler()并未考虑Handler的构造是可以接收Looper的,主要是因为我们使用Handler一般都是在主线程中创建,而主线程默认是创建了Looper的,当我们创建Handler的时候,Handler就可以直接拿到Looper了。

Looper的创建

先看下Looper是如何在主线程中创建的,Looper中有个prepareMainLooper()方法,他就是为主线程创建Looper使用的,此过程是在ActivityThread中main()方法里完成的当然内部还是调用了prepare()方法。初始化Looper的时候,MessageQueue也会被创建。

     private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

由于Looper构造私有,所以创建Looper时通过Looper提供的prepare()方法的,并设置给了ThreadLocal,当然取也是通过ThreadLocal获取的。ThreadLocal接下来会有分析。

   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));
    }
    // 取Looper
   public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
ThreadLocal

前边说了Looper在创建后,使用ThreadLocal来存取,那Handler 是怎么获取到当前线程的Looper对象的呢,这就用到了Java中 java.lang包下的一个类ThreadLocal,本质ThreadLocal不是一个线程,简单说:ThreadLocal的作用是可以在不同的线程中维护同一T数据类型的不同value,比如Looper,主线程中的Looper对象,去子线程中去通过ThreadLocal获取时,是获取不到的,使用ThreadLocal就把Looper和当前的线程绑定到一块了,

  • ThreadLocal是有set和get方法来设置和获取数据的
  • 首先看看ThreadLocal的set方法
  public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

首先set方法获取到了当前的线程,并通过values方法拿到了当前线程的localValues,查看Thread类中的localValues 可以看到
ThreadLocal.Values localValues; Thread是持有ThreadLocal.Values的,如果Thread里的localValues为空的把又调用了initializeValues()方法,这个方法就是new了一个Values赋值给了localValues,

    //ThreadLocal的values方法
        Values values(Thread current) {
        return current.localValues;
    }

得到values后就是开始存数据了,下边是put方法的源码,这个put方法具体逻辑不去分析,不过大体可以理解ThreadLocal的原理:每个Thread线程里都有自己的ThreadLocal.Values,Values存放数据用的是Object[] 数组,这也就理清了ThreadLocal是如何在不同的线程中维护同一数据类型的不同数据副本。


void put(ThreadLocal<?> key, Object value) {
            cleanUp();
            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;
            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];
                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }
                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }
                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }
                ...
            }
        }
  • 根据分析我们来使用ThreadLocal设置下数据看看输出结果,加深下印象
public class ThreadLocalTest {

    class User {
        public String Name;

        @Override
        public String toString() {
            return Name;
        }
    }

    public ThreadLocal<User> mThreadLocal = new ThreadLocal<>();

    public void startThread() {

        System.out.println("start:" + Thread.currentThread().getName() + "--" + mThreadLocal.get());

        new Thread("Thread1") {
            @Override
            public void run() {
                super.run();
                User user = new User();
                user.Name = "线程1";
                mThreadLocal.set(user);
                System.out.println(Thread.currentThread().getName() + "--" + mThreadLocal.get());
            }
        }.start();
        new Thread("Thread2") {
            @Override
            public void run() {
                super.run();
                System.out.println(Thread.currentThread().getName() + "--" + mThreadLocal.get());
            }
        }.start();
        new Thread("Thread3") {
            @Override
            public void run() {
                super.run();
                User user = new User();
                user.Name = "线程3";
                mThreadLocal.set(user);
                System.out.println(Thread.currentThread().getName() + "--" + mThreadLocal.get());
            }
        }.start();

        System.out.println("end:" + Thread.currentThread().getName() + "--" + mThreadLocal.get());
    }

    // 在Main方法里调用startThread()方法后打印出的结果:
    //start:main--null
    //Thread1--线程1
    //Thread2--null
    //Thread3--线程3
    //end:main--null
Looper.loop()

Looper的作用就是循环,ActivityThread创建了Looper后并调用loop()方法开启了循环,


public final class ActivityThread {
    //...
    public static void main(String[] args) {
            ...
            Looper.prepareMainLooper();

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

            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            // End of event ActivityThreadMain.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            Looper.loop();
            ...
        }
    }

以下是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;
            }
            // 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;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            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()方法是个for的无限循环,循环去调用MessageQueue的next()方法获取消息,获取到并交给了Handler处理:msg.target.dispatchMessage(msg)。handler在sendMessage的时候,将自己传给了Message对象,所以此时的msg.target就是Handler,如果取不到Message,loop()方法就会堵塞,因为MessageQueue.next()是个堵塞方法,直到next()方法有了新的消息,loop()才会继续。

MessageQueue

消息队列是实际是一个链表的数据结构,来维护消息的插入和删除,

Handler

Handler我们在使用时都是直接new Handler的方式直接使用,其实Handler还有多个构造重载,使用方式比较多,

  • Handler创建

    public Handler() {
            this(null, false);
    }
    public Handler(Callback callback) {
        this(callback, false);
    }
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
     public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
  • 通过构造可以看出我们以直接new Handler()的方式创建,内部会调用的Looper.myLooper();此方法前面介绍过了,取Looper是通过ThreadLocal.get()方法,所以获取到的是当前所在线程的Looper,即主线程中的Looper。Looper将获取到消息传给Handler处理,因为Looper位于主线程,所以Handler在Looper中处理消息,也就是在主线程中处理消息。
  • 除此Handler还可以接收一个Looper来创建。这里牵扯到一个问题面试常问的问题:Handler可以在子线程中创建么?答案是可以的,但是创建时要先在子线程中创建Looper,否则直接创建Handler会抛出异常,因为子线程中没有Looper,创建Handler就没有意义。
  • 我们实际处理消息是采用重写Handler的handleMessage()方法,当然Handler也为我们提供了自己的处理消息回调方式,public Handler(Callback callback)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值