Handler工作流程
Handler重要函数
Hadler的三个焦点概念
1.消息队列(MessageQueue) 消息队列是一个先进先出(FIFO)的数据结构(在MessageQuene中使用链表来实现的),用于存储待处理的消息。每个 Handler 关联一个消息队列,这个队列负责存储和处理发送到该 Handler 的消息。
2.消息(Message) 消息是 Handler 处理的根本单位,可以对消息进行封装。每个消息都包含一个标识符(what)、处理消息的目标 Handler(target)、可选的数据(obj)以及处理消息的方法(callback)等信息。消息对象通过消息队列在不同线程间传递。
3.Looper Looper 是一个线程局部的类,用于管理消息队列。一个线程只能有一个 Looper,而主线程默认已经有一个 Looper。在子线程中使用 Handler 之前,你通常需要调用 Looper.prepare() 和 Looper.loop() 来初始化和启动消息循环。
Handler:消息的处理者,handler 负责将需要传递的信息封装成Message,发送给Looper,继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler 对象收到该消息后,调用相应的handler 对象的handleMessage()方法对其进行处理。
Handler的工作流程
class Messagequeue中有一个Message messages,Message messages中又有一个Message next,故Messagequeue(队列)中形成了一个链表来存储Message,enqueueMessage用来入队列,如下所示。
- msg.when = when;
- Message p = mMessages;
- boolean needWake;
- if(p == null || when == 0 || when < p.when){
- msg.next = p;
- mMessages = msg;
- needWake = mBlocked;
- }
复制代码- //取消息
- Message next(){
- ......
- Message msg = mMessages;
- ......
- return msg;
- ......
- }
复制代码 next()函数是在loop()函数中(Looper()函数中的loop()),通过loop()函数来实现取消息
- public static void loop(){
- ......
- for(;;){
- Message msg = queue.next();
- ......
- }
- ......
- msg.target.dispacthMessage(msg); //Handler target
- ......
- }
复制代码 调用dispacthMessage()会触发handleMessage(msg)
- public void dispacthMessage(Message msg){
- if(msg.callback != null){
- handleCallback(msg);
- }else{
- if(mCallback != null){
- if(mCallback.handleMessage(msg)){
- return;
- }
- }
- handleMessage(msg);
- }
- }
复制代码 子线程Handler->sendMessage(); queue.enqueueMessage(); sendMessage就是往Messagequeue中添加Message; Messagequeue是用来存储Message的队列(内部用的是链表); handle在looper->loop()函数中有queue.next()会从Messagequeue中取消息; handler->handleMessage(),主线程中取消息。
Handler的创建与使用
创建Handler
在主线程中创建 Handler 时,它会自动与主线程的 Looper 关联。在其他线程中使用 Handler 时,你需要显式地将 Handler 与该线程的 Looper 关联。
- // 在主线程中创建 Handler
- Handler handler = new Handler();
- // 在子线程中创建 Handler
- Handler handler = new Handler(Looper.getMainLooper());
复制代码 发送消息
使用 Handler.sendMessage(Message msg) 方法可以向消息队列发送消息。
- Message message = handler.obtainMessage();
- message.what = MESSAGE_ID;
- message.obj = someObject;
- handler.sendMessage(message);
复制代码 处理消息
通过重写 Handler.handleMessage(Message msg) 方法,可以处理收到的消息。
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // 处理消息
- }
- };
复制代码 使用Handler.post()来发送消息
- // 步骤1:在主线程中创建Handler实例
- private Handler mhandler = new mHandler();
- // 步骤2:在工作线程中 发送消息到消息队列中 & 指定操作UI内容
- // 需传入1个Runnable对象
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ... // 需执行的UI操作
- }
- });
- // 步骤3:开启工作线程(同时启动了Handler)
- // 多线程可采用AsyncTask、继承Thread类、实现Runnable
复制代码 Handler的使用案例
sendMessage()的使用
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Looper;
- import android.os.Message;
- import android.view.View;
- import android.widget.TextView;
- import android.widget.Toast;
-
- import androidx.activity.EdgeToEdge;
- import androidx.annotation.NonNull;
- import androidx.appcompat.app.AppCompatActivity;
-
- public class MainActivity extends AppCompatActivity {
- private TextView tvContent;
- String strFromNet;
-
- private Handler mHandler = new Handler(Looper.myLooper()){
- @Override
- public void handleMessage(@NonNull Message msg) {
- super.handleMessage(msg);
- if(msg.what == 0)
- {
- String strData = (String) msg.obj;
- tvContent.setText(strData);
-
- Toast.makeText(MainActivity.this, "主线程收到消息啦!", Toast.LENGTH_SHORT).show();
-
- }
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- EdgeToEdge.enable(this);
- setContentView(R.layout.activity_main);
-
- tvContent = findViewById(R.id.tv_content);
- }
-
- public void start(View view){
- new Thread(new Runnable() {
- @Override
- public void run() {
- String stringFrommat = getStringFromNet();
-
- Message message = Message.obtain();
- message.what = 0;
- message.obj = stringFrommat;
- mHandler.sendMessage(message);
- }
- }).start();
-
- Toast.makeText(this, "任务完成!", Toast.LENGTH_SHORT).show();
- }
-
- private String getStringFromNet(){
- String result = "";
- StringBuilder stringBuilder = new StringBuilder();
-
- //模拟一个耗时操作
- for(int i = 0; i < 100; i++)
- {
- stringBuilder.append("字符串" + i);
- }
-
- try{
- Thread.sleep(3000);
- }catch(InterruptedException e){
- e.printStackTrace();
- }
-
- result = stringBuilder.toString();
-
- return result;
- }
- }
复制代码 首先点击按钮会触发start函数,在start函数中创建了一个子线程,在子线程中创建消息的内容,即在getStringFromNet()中输入字符串,为了看到结果(子线程只实行run()中的代码,表面的代码都在主线程中实行),睡眠了三秒,之后使用sendMessage()将消息发送到Messagequeue中,随后主线程中使用handleMessage()去Messagequeue中取消息,末了将消息表现出来。
post()的使用方式
- import android.os.Bundle;
- import android.os.Handler;
- import android.view.View;
- import android.widget.TextView;
-
- import androidx.activity.EdgeToEdge;
- import androidx.annotation.Nullable;
- import androidx.appcompat.app.AppCompatActivity;
-
- public class HandlerActivity extends AppCompatActivity {
-
- private Handler handler = new Handler();
- private TextView tvContent;
- String str = "";
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- EdgeToEdge.enable(this);
- setContentView(R.layout.activity_main);
- tvContent = findViewById(R.id.tv_content);
- }
-
- public void start(View view){
- new Thread(){
- @Override
- public void run(){
- //处理信息
- try{
- sleep(3000);
- //这里使用Runnable
- Runnable runnable=new Runnable() {
- @Override
- public void run() {
- StringBuilder stringBuilder = new StringBuilder();
- //模拟一个耗时操作
- for(int i = 0; i < 100; i++)
- {
- stringBuilder.append("字符串" + i);
- }
- str = stringBuilder.toString();
- tvContent.setText(str);
- }
- };
- //这里执行post(Runnable)操作
- handler.post(runnable);
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- }.start();
- }
- }
复制代码 Handler的内部函数源码解析
消息循环(Looper.loop())
- /**
- * 源码分析: Looper.loop()
- * 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
- * 特别注意:
- * a. 主线程的消息循环不允许退出,即无限循环
- * b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
- */
- public static void loop() {
- //......省略
- // 1. 获取当前Looper的消息队列
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- // myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
- // 即loop()执行前必须执行prepare(),从而创建1个Looper实例
- final MessageQueue queue = me.mQueue;
- // 获取Looper实例中的消息队列对象(MessageQueue)
- // 2. 消息循环(通过for循环)
- for (;;) {
- // 2.1 从消息队列中取出消息
- Message msg = queue.next();
- if (msg == null) {
- return;
- }
- // next():取出消息队列里的消息
- // 若取出的消息为空,则线程阻塞
- // 2.2 派发消息到对应的Handler
- msg.target.dispatchMessage(msg);
- // 把消息Message派发给消息对象msg的target属性
- // target属性实际是1个handler对象
- // ->>分析1
- // 3. 释放消息占据的资源
- msg.recycle();
- }
- }
-
- /**
- * 分析1:dispatchMessage(msg)
- * 定义:属于处理者类(Handler)中的方法
- * 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
- */
- public void dispatchMessage(Message msg) {
-
- // 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息(即此处需讨论的)
- // 则执行handleCallback(msg),即回调Runnable对象里复写的run()->> 分析2
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
-
- // 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
- // 则执行handleMessage(msg),即回调复写的handleMessage(msg)
- handleMessage(msg);
-
- }
- }
-
- /**
- * 分析2:handleCallback(msg)
- **/
- private static void handleCallback(Message message) {
- message.callback.run();
- // Message对象的callback属性 = 传入的Runnable对象
- // 即回调Runnable对象里复写的run()
- }
复制代码 loop()里面是一个循环,这样可以不断的获取消息。在没有消息处理的时候Android主线程会进入壅闭,让出cpu时间,比及下一条消息到来的时候(enqueueMessage)会唤醒线程。
消息存储(MessageQueue中的next())
- Message next() {
- int nextPollTimeoutMillis = 0;
- for (;;) {
- nativePollOnce(ptr, nextPollTimeoutMillis);//阻塞
- synchronized (this) {
- // Try to retrieve the next message.Return if found.
- final long now = SystemClock.uptimeMillis();
- Message prevMsg = null;
- Message msg = mMessages;
- //检查链表里面是否存在异步消息 target==null代表这个消息是个异步消息屏障
- 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) {
- // Next message is not ready. Set a timeout to wake up when it is ready.
- 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 {
- // No more messages.
- //链表里面没有消息,永久阻塞,等待唤醒
- nextPollTimeoutMillis = -1;
- }
- //......省略
- mBlocked = true;
- continue;//进入下一次循环
- }
- }
复制代码 在next方法中,从队列中读取消息时,会先检查是否存在同步屏障。如果存在同步屏障,优先返回队列中的异步消息,并将该异步消息从队列中移除。如果队列中没有同步屏障,则返回队列中的同步消息,并将该同步消息从队列中移除
同步消息中会有一个变量when,这个变量存储的就是时间,messageQueue里面的message会按照链表实行的时间次序从链表头到链表尾排序,链表链表头的消息会最先实行。
异步消息:异步指该消息会优于其他平凡消息实行,在异步消息实行完毕之前其他消息无法实行。通过postSyncBarrier()来创建一个屏障,通过removeSyncBarrier() 来移撤消屏障。这样通过message.setAsynchronous(true) 设置的异步消息就会优先实行。
sendMessage()
- /**
- * 源码分析:mHandler.sendMessage(msg)
- * 定义:属于处理器类(Handler)的方法
- * 作用:将消息 发送 到消息队列中(Message ->> MessageQueue)
- */
- public final boolean sendMessage(Message msg)
- {
- return sendMessageDelayed(msg, 0);
- // ->>分析1
- }
- /**
- * 分析1:sendMessageDelayed(msg, 0)
- **/
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- // ->> 分析2
- }
- /**
- * 分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
- **/
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- // 1. 获取对应的消息队列对象(MessageQueue)
- MessageQueue queue = mQueue;
- // 2. 调用了enqueueMessage方法 ->>分析3
- return enqueueMessage(queue, msg, uptimeMillis);
- }
- /**
- * 分析3:enqueueMessage(queue, msg, uptimeMillis)
- **/
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- // 1. 将msg.target赋值为this
- // 即 :把 当前的Handler实例对象作为msg的target属性
- msg.target = this;
- // 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
- // 实际上则是将该消息派发给对应的Handler实例
- // 2. 调用消息队列的enqueueMessage()
- // 即:Handler发送的消息,最终是保存到消息队列->>分析4
- return queue.enqueueMessage(msg, uptimeMillis);
- }
- /**
- * 分析4:queue.enqueueMessage(msg, uptimeMillis)
- * 定义:属于消息队列类(MessageQueue)的方法
- * 作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
- * 采用单链表实现:提高插入消息、删除消息的效率
- */
- boolean enqueueMessage(Message msg, long when) {
- ...// 仅贴出关键代码
- synchronized (this) {
- msg.markInUse();
- msg.when = when;
- Message p = mMessages;
- boolean needWake;
-
- // 判断消息队列里有无消息
- // a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
- 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;
-
- // b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
- 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;
- }
- // 之后,随着Looper对象的无限消息循环
- // 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
- // 最终回调Handler.handleMessage()处理消息
复制代码 post()
- 不需外部创建消息对象,而是内部根据传入的Runnable对象 封装消息对象
- 回调的消息处理方法是:复写Runnable对象的run()
- /**
- * 源码分析:Handler.post(Runnable r)
- * 定义:属于处理者类(Handler)中的方法
- * 作用:定义UI操作、将Runnable对象封装成消息对象 & 发送 到消息队列中(Message ->> MessageQueue)
- * 注:
- * a. 相比sendMessage(),post()最大的不同在于,更新的UI操作可直接在重写的run()中定义
- * b. 实际上,Runnable并无创建新线程,而是发送 消息 到消息队列中
- */
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- // getPostMessage(r) 的源码分析->>分析1
- // sendMessageDelayed()的源码分析 ->>分析2
- }
- /**
- * 分析1:getPostMessage(r)
- * 作用:将传入的Runable对象封装成1个消息对象
- **/
- private static Message getPostMessage(Runnable r) {
- // 1. 创建1个消息对象(Message)
- Message m = Message.obtain();
- // 注:创建Message对象可用关键字new 或 Message.obtain()
- // 建议:使用Message.obtain()创建,
- // 原因:因为Message内部维护了1个Message池,用于Message的复用,使用obtain()直接从池内获取,从而避免使用new重新分配内存
-
- // 2. 将 Runable对象 赋值给消息对象(message)的callback属性
- m.callback = r;
- // 3. 返回该消息对象
- return m;
- } // 回到调用原处
- /**
- * 分析2:sendMessageDelayed(msg, 0)
- * 作用:实际上,从此处开始,则类似方式1 = 将消息入队到消息队列,
- * 即 最终是调用MessageQueue.enqueueMessage()
- **/
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
-
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- // 请看分析3
- }
- /**
- * 分析3:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
- **/
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- // 1. 获取对应的消息队列对象(MessageQueue)
- MessageQueue queue = mQueue;
-
- // 2. 调用了enqueueMessage方法 ->>分析3
- return enqueueMessage(queue, msg, uptimeMillis);
- }
- /**
- * 分析4:enqueueMessage(queue, msg, uptimeMillis)
- **/
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- // 1. 将msg.target赋值为this
- // 即 :把 当前的Handler实例对象作为msg的target属性
- msg.target = this;
- // 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
- // 实际上则是将该消息派发给对应的Handler实例
-
- // 2. 调用消息队列的enqueueMessage()
- // 即:Handler发送的消息,最终是保存到消息队列
- return queue.enqueueMessage(msg, uptimeMillis);
- }
- // 注:实际上从分析2开始,源码 与 sendMessage(Message msg)发送方式相同
复制代码
参考连接:
Android异步通讯:手把手带你深入分析 Handler机制源码 - 简书 (jianshu.com)
Android Handler 源码解析,从入门到险些入门_android handler源码-CSDN博客
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |