Handler

作者: 壹元伍角叁分 | 来源:发表于2021-09-26 22:33 被阅读0次

前言

  1. 线程间如何通讯?

    handler通信实现的方案实际上是内存共享的方案

  2. 为什么线程间不会干扰?

    内存管理设计思路优秀

  3. 为什么wait/notify用武之地不大?

    因为handler已经将需要这部分功能进行了linux层的封装

handler学习点:

源码epoll,设计思路,设计模式,异步消息和同步消息,消息屏障/handlerThread IntentServer

activity的启动:

lancher桌面程序,点击屏幕上的app图标,zygot fork一个进程给每一个应用,给每一个应用分配一个jvm,启动虚拟机。java的main函数就在ActivityThread.main()
给每一个应用都分配一个虚拟机的好处?独立空间、隔离、独立的生命周期

流程分析:

1、Looper的创建


ActivityThread.main(String[] args)
    //初始化环境
--> Environment.initForCurrentUser();
    //启动主线程
--> Looper.prepareMainLooper();
    --> prepare(false);@Looper
            //如果从ThreadLocal获取到了looper,那就不允许修改了。
        --> if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            //不存在则new了一个Looper,并保存在ThreadLocal中。
            sThreadLocal.set(new Looper(quitAllowed));
                //在Looper的私有构造中,MessageQueue进行了初始化。并赋值当前thread给mThread变量
            --> mQueue = new MessageQueue(quitAllowed);
            --> mThread = Thread.currentThread();
        //looper只能被创建一次。
    --> if (sMainLooper != null) {
           throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
        --> return sThreadLocal.get();
    //
--> Looper.loop();
        //获取当前线程中的looper
    --> final Looper me = myLooper();
        --> return sThreadLocal.get();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //每一个线程都有一个looper,每一个looper都有一个MessageQueue
        final MessageQueue queue = me.mQueue;

        //for循环,是个死循环。所有的代码都是在handler上运行的
        //在for循环中进行分发
    --> for (;;) {
           //由MessageQueue从队列中取出消息
           Message msg = queue.next(); 
           //looper相当于是一个永动机,能够让它停止运行的唯一方式就是msg=null。
           //那什么时候回发送一个为null的message呢?1应用退出,2调用quit()
           if (msg == null) {
              return;
           }
    
           //target=handler.
           msg.target.dispatchMessage(msg);
               //处理消息
           --> handleMessage(msg);@Handler
        }

2、handler发送消息

通过MessageQueue.enqueueMessage()将消息添加到队列中

Handler.post(@NonNull Runnable r) 
--> sendMessageDelayed(getPostMessage(r), 0);
    --> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

Handler.sendMessage(@NonNull Runnable r) 
--> sendMessageDelayed(getPostMessage(r), 0);
    --> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

Handler.sendEmptyMessage(int what)
--> sendEmptyMessageDelayed(what, 0);
    --> sendMessageDelayed(msg, delayMillis);
        --> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

最终调用的都是 sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

Handler.sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
--> MessageQueue queue = mQueue;
--> enqueueMessage(queue, msg, uptimeMillis);@Handler
        // msg.target = this = Handler
    --> msg.target = this;
        //将消息放到消息队列
    --> queue.enqueueMessage(msg, uptimeMillis);@MessageQueue
        //队列的插入节点。这个涉及到数据结构与算法。需要去学习下。。。
        --> if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {              
                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;
                prev.next = msg;
            }
            //如果之前是处于阻塞状态下,需要唤醒looper
            if (needWake) {
                nativeWake(mPtr);
            }

3、Looper从队列中取消息

通过MessageQueue.next()将消息从队列中取出

//next(),取消息。返回的是message对象
handler.next():Message@MessageQueue
    //mPtr两个地方被赋值:MessageQueue构造中给一个默认值。然后在调用Looper.quit()后,mQuitting的值会置为true时,mPtr会置为0.
--> final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1;
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //进行休眠nextPollTimeoutMillis。如果为0,则无需等待立即返回。
        //如果nextPollTimeoutMillis为-1,则表示无限等待,直到有事件发生为止。在MessageQueue.
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //同步屏障。这个先不管,下面会讲到。
            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 {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 当没有消息处理的时候,nextPollTimeoutMillis置为-1
                nextPollTimeoutMillis = -1;
            }

            //mQuitting一般都为false。在调用Looper.quit()后,mQuitting的值会置为true。那就会跳出循环
            if (mQuitting) {
                dispose();
                //mPtr置为0,将不再进入这个循环
                --> mPtr = 0;
                return null;
            }
             
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }
        }
    }

4、ThreadLocal保证Looper唯一性(一个线程只能创建一个looper)

4.1 往ThreadLocal中添加元素
ThreadLocal.set(T value) 
--> Thread t = Thread.currentThread();
    //先去获取当前threa的中threadLocals对象,threadLocals是个map集合
    ThreadLocalMap map = getMap(t);
    //Thread.threadLocals,每个thread中都保存着一个ThreadLocalMap
    --> return t.threadLocals;
    
    if (map != null)
       //如果当前thread中的threadLocals不为空,直接设置值。
       map.set(this, value);
    else
       //如果当前thread中的threadLocals为空,则new一个,再保存
       createMap(t, value);
       --> t.threadLocals = new ThreadLocalMap(this, firstValue);
           //ThreadLocalMap中维护着一个map集合table。也就是Entry对象,key=Thread,value=需要保存的值
           --> table = new Entry[INITIAL_CAPACITY];
               int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
               table[i] = new Entry(firstKey, firstValue);
               size = 1;
               setThreshold(INITIAL_CAPACITY);
    }
4.2 从ThreadLocal中取出元素
ThreadLocal.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();

//ThreadLocalMap是ThreadLocal中的内部类。
ThreadLocal.ThreadLocalMap
//Entry的key是
--> static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;

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

其他注意点:

1、子线程中维护的Looper,消息队列无消息的时候,调用Looper.quit(),停止looper的轮巡。

--> mQueue.quit(false);
    --> synchronized (this) {
            if (mQuitting) {
                return;
            }
            //mQuitting置为true,
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }
    
            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }

2、阻塞/唤醒机制
nativePollOnce(ptr, nextPollTimeoutMillis)/nativeWake(mPtr)

相关面试题:

1、一个线程有几个 Handler?
可以随便new

2、一个线程有几个 Looper?如何保证?
一个,私有化了构造方法,需要调用Looper.prepare()创建。先从ThreadLocal中取,如果没有才去new

3、Handler 内存泄漏原因?为什么其他的内部类没有说过有这个问题?怎么解决?
本质原因就是,内部类持有了外部类的对象,且两者生命周期不一致。当外部类需要销毁的时候,由于内部类持有外部类的对象导致不能被回收。
Handler发送消息后,enqueueMessage()中Message持有了handler的引用。而handler又持有了activity的引用。
而Handler可以设置延迟发送。如果设置了20min后执行,那Message就一直持有activity的引用20min。如果activity已经关闭,却无法被回收,
造成内存泄漏。
其他内部类没有这个问题是因为它们的生命周期一致。
解决办法:使用static。因为static的对象不会持有外部类的引用,不影响外部类被回收。

4:为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
new Handler,在handler的构造中,要获取looper,是从ThreadLocal中获取的,如果获取到的looper为null,说明没有被创建,会导致崩溃。
在ActivityThread.main()中,系统帮我们在主线程创建了looper,所以可以直接在主线程中new Hanaler
而如果在子线程中使用handler,需要我们手动去创建looper。调用Looper.prepare()。并开启消息的接收Looper.loop()。

5:既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Hanaler 可能处于不同线程),那它内部是如何确保线程安全的?

6:我们使用 Message 时应该如何创建它?
Message.obtain(),内部维护了message池,防止无序创建导致oom

7:Looper死循环为什么不会导致应用卡死
应用卡死,实际是amr,操作无响应。是说执行某个消息时间过长,而其他需要执行的消息等待了时间过长,没有被执行,才会执行anr。

同步屏障:

如果队列中有很多消息,那系统是如何保证每16s强制刷新一次屏幕呢?
这就要引入 同步屏障 机制。
定义:
什么是异步消息?需要立刻执行的消息,通过MessageQueue.postSyncBarrier()发送的消息。
什么是同步消息?其他方式发送的所有消息。
1、还记得在MessageQueue.next()中有这样一段代码吗?

 Message prevMsg = null;
 Message msg = mMessages;
 //当msg.target = null时进入。那msg.target啥时候被赋值,啥时候不被赋值呢?
 if (msg != null && msg.target == null) {
    //去轮巡消息,找到同步消息。当msg.isAsynchronous()=true的时候退出while循环。
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
 }

2、正常发送的都是同步消息,msg.target = 对应的handler:

Handler.sendMessage(@NonNull Message msg)
--> sendMessageDelayed(msg, 0);
    --> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        --> enqueueMessage(queue, msg, uptimeMillis);
                //将handler赋值给message的target。这个时候msg.target是有值的。那什么时候不被赋值呢
            --> msg.target = this;

3、添加同步屏障。发送异步消息时,msg.target = null

ViewRootImpl.scheduleTraversals()
--> mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //开启同步屏障(发送一个异步消息)
    --> postSyncBarrier(SystemClock.uptimeMillis())
            //直接往消息队列中插入一个异步消息
        --> synchronized (this) {
                final int token = mNextBarrierToken++;
                //这里没有给msg.target赋值。
                final Message msg = Message.obtain();
                msg.markInUse();
                msg.when = when;
                msg.arg1 = token;

                Message prev = null;
                Message p = mMessages;
                if (when != 0) {
                    while (p != null && p.when <= when) {
                        prev = p;
                        p = p.next;
                    }
                }
                if (prev != null) { // invariant: p == prev.next
                    msg.next = p;
                    prev.next = msg;
                } else {
                    msg.next = p;
                    mMessages = msg;
                }
                return token;
            }
        //发送刷新屏幕的同步消息
    --> mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        --> postCallbackDelayed(callbackType, action, token, 0);
            --> postCallbackDelayedInternal(callbackType, action, token, delayMillis);
                --> synchronized (mLock) {
                    final long now = SystemClock.uptimeMillis();
                    final long dueTime = now + delayMillis;
                    mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

                    if (dueTime <= now) {
                        scheduleFrameLocked(now);
                    } else {
                        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                        msg.arg1 = callbackType;
                        msg.setAsynchronous(true);
                        mHandler.sendMessageAtTime(msg, dueTime);
                    }
                }

4、取消异步屏障

void unscheduleTraversals() {
    --> mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        --> synchronized (this) {
                Message prev = null;
                Message p = mMessages;
                while (p != null && (p.target != null || p.arg1 != token)) {
                    prev = p;
                    p = p.next;
                }
                if (p == null) {
                    throw new IllegalStateException("The specified message queue synchronization "
                            + " barrier token has not been posted or has already been removed.");
                }
                final boolean needWake;
                if (prev != null) {
                    prev.next = p.next;
                    needWake = false;
                } else {
                    //给mMessages重新赋值,相当于移除同步屏障
                    mMessages = p.next;
                    needWake = mMessages == null || mMessages.target != null;
                }
                p.recycleUnchecked();

                // If the loop is quitting then it is already awake.
                // We can assume mPtr != 0 when mQuitting is false.
                if (needWake && !mQuitting) {
                    nativeWake(mPtr);
                }
            }
    --> mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

相关文章

网友评论

      本文标题:Handler

      本文链接:https://www.haomeiwen.com/subject/nfbcnltx.html