Connor学Android - Handler机制

在这里插入图片描述

Learn && Live

虚度年华浮萍于世,勤学善思至死不渝

前言

Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/GXMXg,话不多说我们马上开始!

1.概述

(1)Android 的消息机制主要是指 Handler 的运行机制,Handler 的运行需要底层的 MessageQueue 和 Looper 的支撑

(2)Handler 的主要作用是将一个任务切换到某个指定的线程中执行

Handler 的工作原理

(1)创建 Handler

  • Handler 创建时会采用当前线程的 Looper 来构建内部的消息循环系统,如果当前线程没有 Looper,则会报运行时异常
  • 因此创建 Handler 前需要确定当前线程中已有 Looper,否则需要创建

(2)Handler 创建完成后,就可以与内部的 MessageQueue 和 Looper 协同工作了

  • 通过 Handler 的 post 方法将一个 Runnable 传递到 Handler 内部的 Looper 中处理,也可以由 send 方法发送消息到 Looper 中
  • 实际上 post 方法最终也会调用 send 方法完成
  • 当 Handler 的 send 方法被调用时,会调用 MessageQueue 的 enqueueMessage 方法将消息加入到消息队列
  • Looper 发现有新消息到来时,就会将其再交给 Handler 处理
  • Handler 会调用 dispatchMessage 方法拿到之前传递的 Runnable 对象,并调用其 run 方法内的 handleMessage 接收并处理消息

2.分析

1.ThreadLocal 的工作原理

(1)ThreadLocal 是一个线程内部的数据存储类,可以在指定线程中存储数据,只有当前线程可以获取到存储的数据,其他线程不行

(2)Looper、ActivityThread 和 AMS 中都用到了 ThreadLocal

(3)ThreadLocal 主要有两个应用场景

  • 当某些数据是以线程为作用域并且不同线程具有不同的数据副本时,如 Looper 的作用域就是当前线程,通过 ThreadLoacal 就可以轻松实现 Looper 在线程中的存取
  • 可用于复杂逻辑下的对象传递,比如监听器的传递,可以让监听器作为线程内的全局对象,线程内只需通过 get 就可以获取监听器

ThreadLocal 源码分析

(1)底层实现

  • Android 10 版本下的 ThreadLocal 是通过一个改装的 HashMap 实现,名称为 ThreadLocalMap

  • ThreadLocalMap 将 ThreadLocal 的弱引用作为 key 值,将要存储的值作为 value 值

  • 之所以使用 ThreadLocal 的弱引用

    • 首先是根据不同的 ThreadLocal 区分不同的线程
    • 其次是为了能处理较大的数据和保证长期的存储、使用
    • 但由于没有与 ReferenceQueue 关联,因此只有当空间不足时,才能保证删除过时的条目
    • 最后就是方便监控 ThreadLocal 内的数据存储情况,当 key == null 时,因为着这个 ThreadLocal 已空,可从表中删除该项
  • 另外需要声明,在本书的版本中,底层是通过数组实现的,key 存储在 index 处,value 存储在 index + 1 处

public class ThreadLocal<T> {
	static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

(2)set 方法

明确了 ThreadLocal 的底层实现,set 方法就简单了

  • 获取当前线程的 Thread 对象和对应的 ThreadLocalMap
  • 如果 map 为空则创建,否则调用 map.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);
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

(3)get 方法

  • 获取当前线程的 Thread 对象和对应的 ThreadLocalMap

  • 如果 map 不为空,则根据 key ,即当前的 ThreadLocal 对象,查询对应的 value

  • 如果 map 为空,则调用 setInitialValue 方法初始化 map,这个方法与 set 相同,但会返回初始化值,默认为 null

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

private T setInitialValue() {
	T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

protected T initialValue() {
    return null;
}
2.MessageQueue 的工作原理

(1)MessageQueue 的内部实现是一个单链表,通过单链表的数据结构来维护消息队列,在插入和删除上比较有优势

(1)MessageQueue 主要包含两个操作:插入和读取,读取操作伴随着删除操作

(2)插入和读取分别对应的方法分别为 enqueueMessage 方法和 next 方法

  • enqueueMessage:向消息队列中插入一条消息,其内主要完成的就是单链表的插入操作
boolean enqueueMessage(Message msg, long when) {
	if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    synchronized (this) {
    	if (msg.isInUse()) {
           	throw new IllegalStateException(msg + " This message is already in use.");
        }

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

        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
  • next
    • 从消息队列中取出一条消息并将其从消息队列中删除
    • next 内部是一个无限循环,如果消息队列中没有消息,则方法会一直阻塞在这里,直至有新的消息加入
Message next() {
	final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1;
    int nextPollTimeoutMillis = 0;
    for (;;) {
    	if (nextPollTimeoutMillis != 0) {
         	Binder.flushPendingCommands();
     	}

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

            if (mQuitting) {
                dispose();
                return null;
            }

            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                mBlocked = true;
                continue;
            }

            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;

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

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

        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}
3.Looper 的工作原理

(1)Looper 在 Android 消息机制中作用是完成消息循环,不停地从 MessageQueue 中查看是否有新消息,有则立即处理,否则阻塞

构造方法

(1)创建 MessageQueue

(2)保存当前线程的对象

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

创建 Looper

(1)Looper 的创建通过 prepare 方法完成

(2)还提供了 prepateMainLooper 方法,用于给主线程也就是 ActivityThread 创建 Looper,其本质也是通过 prepare 方法实现的

(3)可通过 getMainLooper 方法在任何地方获取主线程的 Looper

退出 Looper

(1)Looper 提供了 quit 和 quitSafaly 方法来退出一个 Looper

(2)quit 会直接退出 Looper

(3)quitSafely 只是设定一个退出标记,然后等待把消息队列中已有的消息处理完后才安全安地退出

(4)Looper 退出后,Handler 发送消息也会随之失败,send 方法返回 false

(5)在子线程中,如果手动为其创建了 Looper,则需要在完成操作后调用 quit 来终止消息循环

启动 Looper

(1)通过 loop 方法启动,只有调用了 loop 后,消息循环才会真正的运转起来

(2)loop 方法是一个死循环,内部调用 MessageQueue 的 next 方法从消息队列中取消息,可能会出现三种情况

  • 无消息,则 next 方法阻塞,进而 Lopper 被阻塞
  • 有消息,调用 Handler 的 dispatchMessage 方法来做进一步处理
  • 返回 null,说明调用了 quit 方法导致消息队列被标记为退出状态,loop 结束死循环

(3)Handler 的 dispatchMessage 方法是在创建 Handler 时所使用的 Looper 中执行的,从而将代码逻辑切换到指定的线程中执行

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }

    me.mInLoop = true;
    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();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    boolean slowDeliveryDetected = false;

    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);
        }
            // Make sure the observer won't change while processing a transaction.
        final Observer observer = sObserver;

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
        	slowDispatchThresholdMs = thresholdOverride;
 	  	    slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
            	Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (slowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    slowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    slowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

        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();
    }
}
4.Handler 的工作原理

(1)Handler 的工作主要包含消息的发送和接收

(2)消息的发送主要通过一系列 post 或 send 方法来实现

(3)消息的接收主要涉及 dispatchMessage 和 handleMessage

dispatchMessage

(1)首先检查 Message 的 callback 是否为 null,这个 callback 实际上是一个 Runnable 对象,就是 post 方法传递的参数

(2)是 null ,调用 handleCallback 方法,方法内部会调用 callback 的 run 方法,也就是执行 post 传递的 Runnable 对象的 run 方法

(3)不是 null,则进一步检查 mCallback 是否为 null

(4)不是 null ,则调用 mCallback 的 handleMessage 方法

handlMessage

(1)上面提到的 Callback 是一个接口,包含了 handleMessage 方法

(2)通常我们会创建一个继承了 Handler 子类的对象并重写 handlMessage 方法来处理具体的消息

(3)这里可见,我们也可以通过实现这个接口并实现 handleMessage 方法来处理具体的消息

5.主线程的消息循环

(1)Activity 的主线程就是 ActivityThread,主线程的入口方法是 main 方法

(2)main 方法中会通过 Looper.prepareMainLooper 方法来创建主线程的 Looper 以及 MessageQueue

(3)通过 Looper.loop 方法来开启主线程的消息循环

(4)主线程的消息循环开始了以后,ActivityThread 会通过一个名为 ActivityThread.H 的 Handler 来和消息队列交互,这个 H 内部定义了一组消息类型,主要包含了四大组件的启动和停止等过程

(5)主线程的消息循环过程

  • ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信
  • AMS 以进程间通信的方式完成 ActivityThread 的请求后会回调 ApplicationThread 中的 Binder 方法
  • 然后 ApplicationThread 会向 H 发送消息
  • H 收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread 中执行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ConnorYan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值