关于Handler我们应该知道的知识
创始人
2024-05-24 16:31:36
0

Handler可以帮助我们在特定的线程执行任务。我们也可以用Handler计划一个任务在未来某个时间点执行。Handler会将我们给它的任务在特定的线程里进行排队执行。如果我们想在某个线程做些什么任务,我们可以指定Handler的Looper,然后Handler就会将这个任务添加到Looper对应的消息队列(message queue)。

        val handler = Handler(Looper.getMainLooper())val runnable = Runnable {println("Hello world")}handler.postDelayed(runnable,3_000L)

创建Looper

我们在某个线程里调用Looper.prepare(),就会给这个线程创建一个Message Queue

Looper.getMainLooper()的有关的代码:
这是应用在启动时,调用的main函数。main函数写在哪里是无所谓的,在计算机操作系统里已约定好程序的入口是main,所以你也会看到它是static的。android的应用程序的main函数都写在ActivityThread.java这个文件里。下面是启用启动时准备主线程使用的Looper:

    public static void main(String[] args) {...Looper.prepareMainLooper();...}

prepareMainLooper()函数:
看到未!它调用了prepare()函数。为当前线程准备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();}}

prepare()函数:
为当前线程(主线程)创建一个Looper,因为是主线程,所以quitAllowed要传false。否则Message Queue里的消息就会有被销毁的风险。

    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的同时会当前线程创建一个MessageQueue:

    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.");}synchronized (this) {if (mQuitting) {return;}mQuitting = true;if (safe) {removeAllFutureMessagesLocked();} else {removeAllMessagesLocked();}// We can assume mPtr != 0 because mQuitting was previously false.nativeWake(mPtr);}}

创建好的Looper会被保存起来:

    public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}

prepare()函数调用完成后Looper创建工作就完成了,调用myLooper()就是去将这个Looper拿回来赋给sMainLooper,Looper.getMainLooper()拿到的就是这个值:

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

到此应用启动后马上为主线程创建好了一个Looper了,通过Looper.getMainLooper()就可以拿到这个主线程的Looper。

public static Looper getMainLooper() {synchronized (Looper.class) {return sMainLooper;}}

顺便提一下,Looper创建出来的MessageQueue是一个链表,它的元素是Message:

public final class MessageQueue {@UnsupportedAppUsageMessage mMessages;...@UnsupportedAppUsageMessage next() {...for (;;) {...synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// Stalled by a barrier.  Find the next asynchronous message in the queue.do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {...} else {// Got a message....if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;...msg.markInUse();return msg;}} ...}}}
}

Handler与Looper的关系

我们拿到了主线程的Looper传给Handler有什么用呢?首先,Handler是android.os包里一个普通的类。
我们创建一个Handler实例,这个实例引用着主线程的Looper:

val handler = Handler(Looper.getMainLooper())
public Handler(@NonNull Looper looper) {this(looper, null, false);
}

通过对主线程Looper的引用,Handler也引用着主线程Looper的MessageQueue:

    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}

Handler怎么使用Looper呢?我以上面为例来说明:

handler.postDelayed(runnable,3_000L)
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {return sendMessageDelayed(getPostMessage(r), delayMillis);
}

getPostMessage()将我们的任务(任务都是用Runnable来写的)生成一个Message:

private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;}

Message.obtain()会先偿试从消息池找一个,如没有就创建一个新的。消息池是一个链表:

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

我们的任务是放在Message的callback字段上的,而Message的target将要放的是我们的Handler对象,请往下继续阅读:

Message{...@UnsupportedAppUsage/*package*/ Runnable callback;@UnsupportedAppUsage/*package*/ Handler target;...
}

sendMessageAtTime()函数,这是一个过渡,做些准备:

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

Handler拿出Looper的MessageQueue,准备做最后的消息入队操作:

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}

Handler对象将自己的引用给了创建出来的Message对象的target字段,然后,通过Looper对象的MessageQueue对象将Message送入队列:

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

至此Handler已完成了它的工作了,那么Looper的MessageQueue里的Message消息什么时候会被取出来操作呢?

Message的执行

main函数最后面有这么一行Looper.loop();,使android应用程序不会退出,因此它会一直循环下去。否则main函数就会被结束。

    public static void main(String[] args) {...Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

loop()函数会拿出当前线程的Looper,比如当前线程是主线程,那么myLooper()拿回来的就是主线程的Looper()

public static void loop() {final Looper me = myLooper();...for (;;) {if (!loopOnce(me, ident, thresholdOverride)) {return;}}}

从当前线程的Looper的MessageQueue取出一个Message,然后把Message给Message的target字段记录的Handler对象处理:

    private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {Message msg = me.mQueue.next(); // might block...try {msg.target.dispatchMessage(msg);...  } ...return true;}

Handler的dispatchMessage()方法,如果callback不是null就是去执行,callback记录的是我们的任务:

 public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}
    private static void handleCallback(Message message) {message.callback.run();}

如果msg.callback是null,且在创建Handler对象时有传个callback的话,就会先调用这个callback再调用handleMessage()方法:

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)
    public void handleMessage(@NonNull Message msg) {}

handleMessage()是一个空方法,所在此方面的实践,往往需要我们重写它,例子:

val handler = object: Handler(Looper.getMainLooper()){override fun handleMessage(msg: Message) {println("Hello world")}}val msg = Message.obtain()
handler.sendMessage(msg)

到此我们知道,任务在哪个线程执行的关键就是prepare()在哪里被执行,也就是为哪个线程创建Looper,发送到这个Looper的任务就会在相应的线程里执行。

runOnUiThread到底做了什么?

class Activity  ... {...final Handler mHandler = new Handler();...public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) {mHandler.post(action);} else {action.run();}}
}
    public final boolean post(@NonNull Runnable r) {return  sendMessageDelayed(getPostMessage(r), 0);}

Activity实例创建时,也会构造一个Handler实例,runOnUiThread()就是利用这个Handler实例向主线程的Looper发现任务。

Handler的知识大体是这样,更多其他涉及的知识,我们以后再分享啦。

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...