欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android 消息传递机制

程序员文章站 2022-07-14 20:39:40
...

1 源码分析

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

一般来说我们都是用Handler()来生成Handler实例的那么我们来看看这个构造函数做了些什么吧:

public Handler() {
        this(null, false);
    }
public Handler(Callback callback, boolean async) {
    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;
    }

mLooper = Looper.myLooper();是什么意思,我们来看看它是怎么实现的

  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

线程本地存储区(==Thread Local Storage,简称为TLS==),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域,这样问题就来了,为什么不同线程之间彼此不能访问对方的TLS区域?我们来看看ThreadLocal的set方法:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

在这个方法内部我们看到,首先通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。这又有了一个疑问,这个set方法不会发生并发嘛?线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。==因为每个线程的变量都是自己特有的,完全不会有并发错误==(当时不懂这句,自己写还是比较直观):

static class A implements Runnable {
       int i;
       @Override
       public void run() {
           for(int j=0;j<=2;j++){
               i+=j;
           }
           System.out.println(i);
       }
   }

还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。接着我们来看看get方法:

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

从上面这代码块可以知道,get是从ThreadLocalMap.Entry拿出来的,也是通过set放进去的,那在哪进行set的呢:

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

当sThreadLocal里面没有东西的时候才会执行set,否则会RuntimeException,到这里来总结下,也就是说使用handler,必须先执行Loop.prepare,问题来了:
- 仔细看看这个方法prepare的参数有什么意义?

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        。。。
    }

quitAllowed这个参数所经过的方法,明显,mQuitAllowed只有Ui线程会设为false,这也是prepare方法默认为true的原因吧

  • 为什么我们直接使用Handler的时候不需要执行这个方法呢

回到最开始Handler()这个构造函数,一般来说我们都是在UI线程中直接使用这个构造函数,从前面的分析我们应该知道,要使用Handler这个消息传递,那就应该提前使用prepare方法,我们就来做个实验,在UI线程中使用这个方法,不出意料,闪退:

Caused by: java.lang.RuntimeException: 
        Only one Looper may be created per thread 
        at android.os.Looper.prepare(Looper.java:90)
        at android.os.Looper.prepare(Looper.java:85)

也就是上面sThreadLocal.get() != null这个条件,那肯定是在哪里已经执行过了,答案就是在ActivityThread的main函数里面:Looper.prepareMainLooper();ActivityThread6103行:

 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

注意到prepare里面的参数了嘛,上面也分析了只有在mian
thread中才会为false。
实例化Handler后我们可以用很多方法来发送消息,但最终都是通过enqueueMessage方法来传递消息的:

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

实际上是执行MessageQueue.enqueueMessage:

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

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

            msg.markInUse();
            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;
    }

mQuitting,MessageQueue默认为false,那么哪里赋为true的呢。通过查找可以找到只有在quit(boolean safe)方法中,而且找不到赋为fasle的地方,这不就说明了当执行了MessageQueue.quit这个方法,MessageQueue也会被回收掉的,接着看后面msg.recycle():

public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }
void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

看上去有点晕啊,先来看看Message,这个类的构造函数有很多,要说的就两个:

 public static Message obtain()
 public Message()

尽量不用第二种构造方式,先来分析分析第一种:

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

sPool哪里有赋值呢,通过查找,也就只有上面的recycleUnchecked方法进行赋值了,综合来看,Message对象好像是被循环利用了,根据它的next属性,就知道它是链表的数据结构,这里用到了享元模式,如果用的第二种构造方式,就得重新分配内存了,
接着分析MessageQueue.enqueueMessage,mMessages属性可以说是MessageQueue的头,最先处理的消息。

接着看这个判断语句(p == null || when == 0 || when < p.when):p是当前的message,when表示消息处理的时间,它的值是距离开机的时间+延迟的时间,when

 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;
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

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

关键的代码就是msg.target.dispatchMessage(msg);target就是Handler,那么久回到Handler.dispatchMessage方法中:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
 public void handleMessage(Message msg) {
 //空方法
    }

这些就是整个消息传递机制了

2 参考: