Handler处理消息相关源码解析

Posted 涂程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler处理消息相关源码解析相关的知识,希望对你有一定的参考价值。

一、handler的用法

1.1、用于线程切换

Handler handler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
    @Override
    public void run() {
        handler.post(() -> {
            binding.tv.setText("hello world");
        });
    }
}).start();

1.2、线程间通信

为了防止handler导致内存泄露,handler对Activity采用弱引用,因弱引用不会影响Activity的java生命周期。

static class MyHandler extends Handler {
    WeakReference<MainActivity> mActivityWeakReference;public MyHandler(MainActivity activity) {
        mActivityWeakReference = new WeakReference<>(activity);
    }
    @Override
    public void handleMessage(@NonNull Message msg) {
        MainActivity activity = mActivityWeakReference.get();
        if (activity != null && msg!=null && msg.obj!=null) {
             //todo 
        }
    }
}
MyHandler myHandler = new MyHandler(MainActivity.this);
new Thread(new Runnable() {
    @Override
    public void run() {
        Message message = Message.obtain();
        message.obj = "aaa";
        mMyHandler.sendMessage(message);
    }
}).start();

1.3、任务延时执行

static class MyRunnalbe implements Runnable {
    @Override
    public void run() {
        Log.e("test", "test memory leak?");
    }
}
//发送延时任务
Handler handler3 = new Handler(Looper.getMainLooper());
handler3.postDelayed(new MyRunnalbe(), 10000);

最后大家不要忘了,一定记得在Activity的onDestroy及时解除Message对Handler的强引用。

@Override
protected void onDestroy() {
    super.onDestroy();
    mMyHandler.removeCallbacksAndMessages(null);
}

二、Handler(Loope(MessageQueue))的创建

2.1、Handler创建

先看看handler的构造函数。

构造函数肯定是相互调用,只需要看最多的参数的那个

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { xxxx }
  • 从构造可以看出,Handler的创建需要Looper,没有Looper玩不转呐,我们之前写handler用法的时候,都是在主线程创建Handler,它构造里面会调用Looper.myLoop()获取Looper。

  • ThreadLocal可用于线程间的数据隔离,这是解决多线程并发的一种手段 (不共享可变资源)。将线程需要的Looper保存在ThreadLocal里面。

  • 我们App的入口函数就是ActivityThread的main方法,一般在main方法里面就做两件事,第一件就是启动创建主线程的Looper,启动Looper轮询;第二件事就是将ApplicationThread的binder对象注册给AMS(AMS拿到它后就可以调用App的方法)。loop轮询一旦跳出了死循环,App直接就抛异常了。

  • 如果最后一个参数async为true,那么通过handler.sendMessage的message都会打上异步消息的标签。

//from Handler
public Handler(@Nullable Callback callback, boolean async) {
   .....  
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
           "Can't create handler inside thread " + Thread.currentThread()
                                              + " that has not called Looper.prepare()");
    }
  .......
}

//from Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//myLooper直接从Threadlocal中取
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
//创建主线程Looper,因为主线程Looper不支持退出
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));
}

//from ActivityThread
public static void main(String[] args) {
    ....... 
    //创建主线程Looper
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    //向AMS注册,将app的binder对象给AMS
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    .....
    //启动轮询
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Q: 如果直接在子线程里面new Handler()创建,从ThreadLocals里面肯定是取不到对应的looper的,所以会抛异常。

需要显式调用Looper.prepare()才行。

子线程中弹吐司也要先通过Looper.prepare()创建Looper,不然也会奔溃。

原因同上👆

2.1.1、ThreadLocal原理

都说ThreadLocal可用于线程间数据隔离,完美的解决并发问题,那啥原理呢? 先看看用法。

public void set(T value) {
    Thread t = Thread.currentThread(); //获取当前线程
    ThreadLocalMap map = t.threadLocals; //线程的t.threadLocals
    if (map != null)
        map.set(this, value);  //直接添加
    else
        createMap(t, value);   //那就给当前线程创建这个ThreadLocalMap类型的t.threadLocals成员
}
private void set(ThreadLocal<?> key, Object value) {
    Entry[] tab = table;
    int len = tab.length; 
    int i = key.threadLocalHashCode & (len-1); //斐波那契散列算法
    for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) { //开放寻址法解决冲突
        ThreadLocal<?> k = e.get();    
        if (k == key) {
            e.value = value;
            return;
        }
        if (k == null) {
            replaceStaleEntry(key, value, i); 
            return;
        }
    }
    tab[i] = new Entry(key, value); //key就是ThreadLocal
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)  rehash();
}
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

threadlocal.set(xxx)时,会从当前线程(主线程/子线程)取出它的ThreadLocalMap类型的成员threadLocals,不为null就直接添加了,为空则先创建。ThreadLocalMap底层采用数组实现,不像HashMap采用数组+链表/红黑树实现。这里的元素的index采用斐波那契散列算法实现,采用开放寻址法解决散列冲突,具体参考ThreadLocalMap的hash算法。数组元素Entry很有特点。

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

Entry是一个弱引用类,直接通过entry.get()可以获取到Threadlocal引用,因其作为entry的key,只能对应一个值value。同时也参与table数组index的hash算法(i = key.threadLocalHashCode & (len-1))。如通过entry.get()获取threadLocal为null时,此时value不为null,就有发生泄漏可能,一般建议就是用完及时进行remove掉。

2.2、Looper的创建

public static void prepare() {
    prepare(true);
}
public static void prepare() {
    prepare(true);
}
//quitAllowed 是否支持退出,默认是支持退出的。但是主线程的looper不支持退出。
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));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();

Handler创建需要Looper,Looper创建需要MessageQueue。如果多次调用Loopr.prepare来创建Looper会报错,即在一个线程里面创建多个Looper会报错

主线程的Looper当然不支持退出,不然随便调用looper.quit,App就奔溃了。用户子线程中通过Looper.prepare()创建Looper默认是true,支持退出的。

2.3、MessageQueue的创建

private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

MessageQueue创建时,创建了一个long类型的成员,native可以通过这个long指针直接强转成native对象用,这里是底层的NativeMessageQueue的指针

从这里可以看出,一个线程可以创建无数个Handler,但只能有一个Looper,一个MessageQueue

三、Looper.loop() 与 handler.sendMessage(xx)

3.1、开启轮询消息Looper.loop()

入口函数ActivityThread.main()方法中,就开启了Looper.loop()。

//from Looper
public static void loop() {
    ......
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // 可能阻塞
        ......
        msg.target.dispatchMessage(msg);
       ...... 
        msg.recycleUnchecked(); //消息进入循环池
    }
}

Q: 如果让我们写个Handler分发、处理Message,你会怎么写?
因为Handler的分发事件可能在其他线程,被称为生产者,处理Message的Handler被称为消费者,典型的生产消费模型,肯定会考虑生产多了+消费慢,生产少了+消费快的场景,此时伪代码如下:

public synchronized void loop() {
    long now = System.currentTimeMillis();
    int count;                                   //mMessageQueue.top()就是获得一个Message
    while ((count = mMessageQueue.size()) == 0 || mMessageQueue.top().when > now) {
        long diff = (count == 0) ? 0 : (now - mMessageQueue.top().when);
        try {
            this.wait(diff);           //这里就类似Message msg = queue.next();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    ......
    this.notifyAll();
}

系统肯定不会用wait/notify+synchronized这种性能欠佳的组合实现,对的,底层用的是Epoll机制实现
来看看MessageQueue的Next方法。

Message next() {
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();  //被调用说明后面的代码可能会引起线程阻塞(此处不懂)
        }
        nativePollOnce(ptr, nextPollTimeoutMillis)  //epoll_wait
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            /此处msg没有target(handler),那就表示这个msg为同步屏障消息,配合异步消息使用的
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;    
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //获取需要等待的时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                     //终于拿到一个可用的Message了,单链表取出Message的操作,模板代码
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;  //所以mMessage就是下一个要处理的Message。记住了
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                nextPollTimeoutMillis = -1;  //-1传给native的epoll_wait,表示永远不能唤醒,需要主动被wake
            }
              //messageQueue为empty了 或者 mMessage是将来才处理的(when还没到)
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                mBlocked = true;   //那就继续阻塞
                continue;
            }
            //每次处理的handler最多只能是4个,多了不处理????
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = idler.queueIdle(); //idleHandler的queueIdle返回值true/false处理不同
            if (!keep) {                      
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        pendingIdleHandlerCount = 0以上是关于Handler处理消息相关源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Android Handler消息机制源码解析

Handler与异步消息的源码解析

Handler与异步消息的源码解析

Android消息机制Handler解析(源码+Demo)

Android开发人员必看的 Handler 消息处理机制(源码实战)

Android开发人员必看的 Handler 消息处理机制(源码实战)