您的当前位置:首页正文

Android中Handler介绍

2024-10-23 来源:个人技术集锦

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 messagesMessage 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()

/** 
   * 源码分析: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)

显示全文