Android中Handler介绍

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

  1. 不需外部创建消息对象,而是内部根据传入的Runnable对象 封装消息对象

  2. 回调的消息处理方法是:复写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博客

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Handler 是一种用于实现线程间通信的机制。它允许我们在一个线程发送消息(Message)或者 Runnable 对象到消息队列(MessageQueue),然后在另一个线程处理这些消息。Handler 的主要机制包括以下几个部分: 1. 消息队列(MessageQueue):用于存储消息的队列,它是一个先进先出(FIFO)的队列。 2. 消息处理器(MessageHandler):用于处理消息的对象,它通常是一个 Handler 的子类,通过重写 handleMessage 方法来处理消息。 3. Looper:用于管理消息队列,它会不断地从消息队列取出消息,然后将其分发给消息处理器进行处理。每个线程只能有一个 Looper。 4. 发送消息(post):将消息发送到消息队列,可以通过 Handler.post 方法发送 Runnable 对象或者 Message 对象。 5. 处理消息(handleMessage):当消息队列有消息时,消息处理器会调用 handleMessage 方法来处理消息。 通过这些机制,我们可以在主线程创建一个 Handler 对象,然后通过 post 方法将耗时操作放到子线程执行,最后通过 handleMessage 方法将执行结果传递回主线程进行更新 UI 界面等操作。 需要注意的是,在 Android ,主线程也称为 UI 线程,因为它主要负责更新 UI 界面。如果在主线程执行耗时操作,会导致界面卡顿,影响用户体验。因此,我们需要使用 Handler 将耗时操作放到子线程执行,然后通过 Handler 在主线程更新 UI 界面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值