Android中Handler介绍

打印 上一主题 下一主题

主题 567|帖子 567|积分 1701

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用来入队列,如下所示。
  1. msg.when = when;
  2. Message p = mMessages;
  3. boolean needWake;
  4. if(p == null || when == 0 || when < p.when){
  5.         msg.next = p;
  6.         mMessages = msg;
  7.         needWake = mBlocked;
  8. }
复制代码
  1.  //取消息
  2.  Message next(){
  3.      ......
  4.      Message msg = mMessages;
  5.      ......
  6.      return msg;
  7.      ......
  8.  }
复制代码
next()函数是在loop()函数中(Looper()函数中的loop()),通过loop()函数来实现取消息
  1.  public static void loop(){
  2.      ......
  3.      for(;;){
  4.          Message msg = queue.next();
  5.           ......
  6.      }
  7.      ......
  8.      msg.target.dispacthMessage(msg);    //Handler target
  9.      ......
  10.  }
复制代码
调用dispacthMessage()会触发handleMessage(msg)
  1.  public void dispacthMessage(Message msg){
  2.      if(msg.callback != null){
  3.          handleCallback(msg);
  4.      }else{
  5.          if(mCallback != null){
  6.              if(mCallback.handleMessage(msg)){
  7.                  return;
  8.              }
  9.          }
  10.          handleMessage(msg);
  11.      }
  12.  }
复制代码
子线程Handler->sendMessage(); queue.enqueueMessage(); sendMessage就是往Messagequeue中添加Message; Messagequeue是用来存储Message的队列(内部用的是链表); handle在looper->loop()函数中有queue.next()会从Messagequeue中取消息; handler->handleMessage(),主线程中取消息。
Handler的创建与使用

创建Handler

在主线程中创建 Handler 时,它会自动与主线程的 Looper 关联。在其他线程中使用 Handler 时,你需要显式地将 Handler 与该线程的 Looper 关联。
  1.  // 在主线程中创建 Handler
  2.  Handler handler = new Handler();
  3.  // 在子线程中创建 Handler
  4.  Handler handler = new Handler(Looper.getMainLooper());
复制代码
发送消息

使用 Handler.sendMessage(Message msg) 方法可以向消息队列发送消息。
  1.  Message message = handler.obtainMessage();
  2.  message.what = MESSAGE_ID;
  3.  message.obj = someObject;
  4.  handler.sendMessage(message);
复制代码
处理消息

通过重写 Handler.handleMessage(Message msg) 方法,可以处理收到的消息。
  1.  Handler handler = new Handler() {
  2.      @Override
  3.      public void handleMessage(Message msg) {
  4.          // 处理消息
  5.      }
  6.  };
复制代码
使用Handler.post()来发送消息
  1.  // 步骤1:在主线程中创建Handler实例
  2.      private Handler mhandler = new mHandler();
  3.  // 步骤2:在工作线程中 发送消息到消息队列中 & 指定操作UI内容
  4.  // 需传入1个Runnable对象
  5.      mHandler.post(new Runnable() {
  6.              @Override
  7.              public void run() {
  8.                  ... // 需执行的UI操作
  9.              }
  10.      });
  11.  // 步骤3:开启工作线程(同时启动了Handler)
  12.  // 多线程可采用AsyncTask、继承Thread类、实现Runnable
复制代码
Handler的使用案例

sendMessage()的使用

  1. import android.os.Bundle;
  2.  import android.os.Handler;
  3.  import android.os.Looper;
  4.  import android.os.Message;
  5.  import android.view.View;
  6.  import android.widget.TextView;
  7.  import android.widget.Toast;
  8.  ​
  9.  import androidx.activity.EdgeToEdge;
  10.  import androidx.annotation.NonNull;
  11.  import androidx.appcompat.app.AppCompatActivity;
  12.  ​
  13.  public class MainActivity extends AppCompatActivity {
  14.      private TextView tvContent;
  15.      String strFromNet;
  16.  ​
  17.      private Handler mHandler = new Handler(Looper.myLooper()){
  18.          @Override
  19.          public void handleMessage(@NonNull Message msg) {
  20.              super.handleMessage(msg);
  21.              if(msg.what == 0)
  22.              {
  23.                  String strData = (String) msg.obj;
  24.                  tvContent.setText(strData);
  25.  ​
  26.                  Toast.makeText(MainActivity.this, "主线程收到消息啦!", Toast.LENGTH_SHORT).show();
  27.  ​
  28.              }
  29.          }
  30.      };
  31.  ​
  32.      @Override
  33.      protected void onCreate(Bundle savedInstanceState) {
  34.          super.onCreate(savedInstanceState);
  35.          EdgeToEdge.enable(this);
  36.          setContentView(R.layout.activity_main);
  37.  ​
  38.          tvContent = findViewById(R.id.tv_content);
  39.      }
  40.  ​
  41.      public void start(View view){
  42.          new Thread(new Runnable() {
  43.              @Override
  44.              public void run() {
  45.                  String stringFrommat = getStringFromNet();
  46.  ​
  47.                  Message message = Message.obtain();
  48.                  message.what = 0;
  49.                  message.obj = stringFrommat;
  50.                  mHandler.sendMessage(message);
  51.              }
  52.          }).start();
  53.  ​
  54.          Toast.makeText(this, "任务完成!", Toast.LENGTH_SHORT).show();
  55.      }
  56.  ​
  57.      private String getStringFromNet(){
  58.          String result = "";
  59.          StringBuilder stringBuilder = new StringBuilder();
  60.  ​
  61.          //模拟一个耗时操作
  62.          for(int i = 0; i < 100; i++)
  63.          {
  64.              stringBuilder.append("字符串" + i);
  65.          }
  66.  ​
  67.          try{
  68.              Thread.sleep(3000);
  69.          }catch(InterruptedException e){
  70.              e.printStackTrace();
  71.          }
  72.  ​
  73.          result = stringBuilder.toString();
  74.  ​
  75.          return result;
  76.      }
  77.  }
复制代码
首先点击按钮会触发start函数,在start函数中创建了一个子线程,在子线程中创建消息的内容,即在getStringFromNet()中输入字符串,为了看到结果(子线程只实行run()中的代码,表面的代码都在主线程中实行),睡眠了三秒,之后使用sendMessage()将消息发送到Messagequeue中,随后主线程中使用handleMessage()去Messagequeue中取消息,末了将消息表现出来。
post()的使用方式

  1. import android.os.Bundle;
  2.  import android.os.Handler;
  3.  import android.view.View;
  4.  import android.widget.TextView;
  5.  ​
  6.  import androidx.activity.EdgeToEdge;
  7.  import androidx.annotation.Nullable;
  8.  import androidx.appcompat.app.AppCompatActivity;
  9.  ​
  10.  public class HandlerActivity extends AppCompatActivity {
  11.  ​
  12.      private Handler handler = new Handler();
  13.      private TextView tvContent;
  14.      String str = "";
  15.      @Override
  16.      protected void onCreate(@Nullable Bundle savedInstanceState) {
  17.          super.onCreate(savedInstanceState);
  18.          EdgeToEdge.enable(this);
  19.          setContentView(R.layout.activity_main);
  20.          tvContent = findViewById(R.id.tv_content);
  21.      }
  22.  ​
  23.      public void start(View view){
  24.          new Thread(){
  25.              @Override
  26.              public void run(){
  27.                  //处理信息
  28.                  try{
  29.                      sleep(3000);
  30.                      //这里使用Runnable
  31.                      Runnable runnable=new Runnable() {
  32.                          @Override
  33.                          public void run() {
  34.                              StringBuilder stringBuilder = new StringBuilder();
  35.                              //模拟一个耗时操作
  36.                              for(int i = 0; i < 100; i++)
  37.                              {
  38.                                  stringBuilder.append("字符串" + i);
  39.                              }
  40.                              str = stringBuilder.toString();
  41.                              tvContent.setText(str);
  42.                          }
  43.                      };
  44.                      //这里执行post(Runnable)操作
  45.                      handler.post(runnable);
  46.                  }catch (Exception e){
  47.                      e.printStackTrace();
  48.                  }
  49.              }
  50.          }.start();
  51.      }
  52.  }
复制代码
Handler的内部函数源码解析

消息循环(Looper.loop())

  1. /**
  2.    * 源码分析: Looper.loop()
  3.    * 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
  4.    * 特别注意:
  5.    *       a. 主线程的消息循环不允许退出,即无限循环
  6.    *       b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
  7.    */
  8.    public static void loop() {
  9.          //......省略
  10.          // 1. 获取当前Looper的消息队列
  11.              final Looper me = myLooper();
  12.              if (me == null) {
  13.                  throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  14.              }
  15.              // myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
  16.              // 即loop()执行前必须执行prepare(),从而创建1个Looper实例
  17.              final MessageQueue queue = me.mQueue;
  18.              // 获取Looper实例中的消息队列对象(MessageQueue)
  19.              // 2. 消息循环(通过for循环)
  20.              for (;;) {
  21.                  // 2.1 从消息队列中取出消息
  22.                  Message msg = queue.next();
  23.                  if (msg == null) {
  24.                      return;
  25.              }
  26.              // next():取出消息队列里的消息
  27.              // 若取出的消息为空,则线程阻塞
  28.              // 2.2 派发消息到对应的Handler
  29.              msg.target.dispatchMessage(msg);
  30.              // 把消息Message派发给消息对象msg的target属性
  31.              // target属性实际是1个handler对象
  32.              // ->>分析1
  33.          // 3. 释放消息占据的资源
  34.          msg.recycle();
  35.          }
  36.  }
  37.  ​
  38.  /**
  39.    * 分析1:dispatchMessage(msg)
  40.    * 定义:属于处理者类(Handler)中的方法
  41.    * 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
  42.    */
  43.    public void dispatchMessage(Message msg) {
  44.  ​
  45.      // 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息(即此处需讨论的)
  46.      // 则执行handleCallback(msg),即回调Runnable对象里复写的run()->> 分析2
  47.          if (msg.callback != null) {
  48.              handleCallback(msg);
  49.          } else {
  50.              if (mCallback != null) {
  51.                  if (mCallback.handleMessage(msg)) {
  52.                      return;
  53.                  }
  54.              }
  55.  ​
  56.              // 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
  57.              // 则执行handleMessage(msg),即回调复写的handleMessage(msg)
  58.              handleMessage(msg);
  59.  ​
  60.          }
  61.      }
  62.  ​
  63.    /**
  64.      * 分析2:handleCallback(msg)
  65.      **/
  66.      private static void handleCallback(Message message) {
  67.          message.callback.run();
  68.          //  Message对象的callback属性 = 传入的Runnable对象
  69.          // 即回调Runnable对象里复写的run()
  70.      }
复制代码
loop()里面是一个循环,这样可以不断的获取消息。在没有消息处理的时候Android主线程会进入壅闭,让出cpu时间,比及下一条消息到来的时候(enqueueMessage)会唤醒线程。
消息存储(MessageQueue中的next())

  1.  Message next() {
  2.      int nextPollTimeoutMillis = 0;
  3.       for (;;) {
  4.           nativePollOnce(ptr, nextPollTimeoutMillis);//阻塞
  5.           synchronized (this) {
  6.                  // Try to retrieve the next message.Return if found.
  7.                  final long now = SystemClock.uptimeMillis();
  8.                  Message prevMsg = null;
  9.                  Message msg = mMessages;
  10.                  //检查链表里面是否存在异步消息 target==null代表这个消息是个异步消息屏障
  11.                  if (msg != null && msg.target == null) {
  12.                  // Stalled by a barrier.  Find the next asynchronous message in the queue.
  13.                      do {
  14.                          prevMsg = msg;
  15.                          msg = msg.next;
  16.                      } while (msg != null && !msg.isAsynchronous());
  17.                  }
  18.               //获取到消息:可能是普通消息 也可能是异步消息
  19.                  if (msg != null) {
  20.                    //消息执行时间未到
  21.                      if (now < msg.when) {
  22.                   // Next message is not ready.  Set a timeout to wake up when it is ready.
  23.                          nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
  24.                      } else {
  25.                          //获取到消息
  26.                          // Got a message.
  27.                          //把消息从链表中删除
  28.                          mBlocked = false;
  29.                          if (prevMsg != null) {
  30.                              prevMsg.next = msg.next;
  31.                          } else {
  32.                              mMessages = msg.next;
  33.                          }
  34.                          msg.next = null;
  35.                          if (DEBUG) Log.v(TAG, "Returning message: " + msg);
  36.                          //标记消息正在使用
  37.                          msg.markInUse();
  38.                          //返回这条消息
  39.                          return msg;
  40.                      }
  41.                  } else {
  42.                      // No more messages.
  43.                      //链表里面没有消息,永久阻塞,等待唤醒
  44.                      nextPollTimeoutMillis = -1;
  45.                  }
  46.                //......省略
  47.                mBlocked = true;
  48.                continue;//进入下一次循环
  49.           }
  50.  }
复制代码
在next方法中,从队列中读取消息时,会先检查是否存在同步屏障。如果存在同步屏障,优先返回队列中的异步消息,并将该异步消息从队列中移除。如果队列中没有同步屏障,则返回队列中的同步消息,并将该同步消息从队列中移除
同步消息中会有一个变量when,这个变量存储的就是时间,messageQueue里面的message会按照链表实行的时间次序从链表头到链表尾排序,链表链表头的消息会最先实行。
异步消息:异步指该消息会优于其他平凡消息实行,在异步消息实行完毕之前其他消息无法实行。通过postSyncBarrier()来创建一个屏障,通过removeSyncBarrier() 来移撤消屏障。这样通过message.setAsynchronous(true) 设置的异步消息就会优先实行。
sendMessage()

  1. /**
  2.    * 源码分析:mHandler.sendMessage(msg)
  3.    * 定义:属于处理器类(Handler)的方法
  4.    * 作用:将消息 发送 到消息队列中(Message ->> MessageQueue)
  5.    */
  6.    public final boolean sendMessage(Message msg)
  7.      {
  8.          return sendMessageDelayed(msg, 0);
  9.          // ->>分析1
  10.      }
  11.           /**
  12.             * 分析1:sendMessageDelayed(msg, 0)
  13.             **/
  14.             public final boolean sendMessageDelayed(Message msg, long delayMillis)
  15.              {
  16.                  if (delayMillis < 0) {
  17.                      delayMillis = 0;
  18.                  }
  19.                  return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  20.                  // ->> 分析2
  21.              }
  22.           /**
  23.             * 分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
  24.             **/
  25.             public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  26.                      // 1. 获取对应的消息队列对象(MessageQueue)
  27.                      MessageQueue queue = mQueue;
  28.                      // 2. 调用了enqueueMessage方法 ->>分析3
  29.                      return enqueueMessage(queue, msg, uptimeMillis);
  30.                  }
  31.           /**
  32.             * 分析3:enqueueMessage(queue, msg, uptimeMillis)
  33.             **/
  34.              private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  35.                   // 1. 将msg.target赋值为this
  36.                   // 即 :把 当前的Handler实例对象作为msg的target属性
  37.                   msg.target = this;
  38.                   // 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
  39.                   // 实际上则是将该消息派发给对应的Handler实例        
  40.                  // 2. 调用消息队列的enqueueMessage()
  41.                  // 即:Handler发送的消息,最终是保存到消息队列->>分析4
  42.                  return queue.enqueueMessage(msg, uptimeMillis);
  43.          }
  44.          /**
  45.            * 分析4:queue.enqueueMessage(msg, uptimeMillis)
  46.            * 定义:属于消息队列类(MessageQueue)的方法
  47.            * 作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
  48.            * 采用单链表实现:提高插入消息、删除消息的效率
  49.            */
  50.            boolean enqueueMessage(Message msg, long when) {
  51.                  ...// 仅贴出关键代码
  52.                  synchronized (this) {
  53.                      msg.markInUse();
  54.                      msg.when = when;
  55.                      Message p = mMessages;
  56.                      boolean needWake;
  57.  ​
  58.                     // 判断消息队列里有无消息
  59.                     // a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
  60.                          if (p == null || when == 0 || when < p.when) {
  61.                              msg.next = p;
  62.                              mMessages = msg;
  63.                              needWake = mBlocked;
  64.                          } else {
  65.                              needWake = mBlocked && p.target == null && msg.isAsynchronous();
  66.                              Message prev;
  67.  ​
  68.                       // b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
  69.                              for (;;) {
  70.                                  prev = p;
  71.                                  p = p.next;
  72.                                  if (p == null || when < p.when) {
  73.                                      break;
  74.                                  }
  75.                                  if (needWake && p.isAsynchronous()) {
  76.                                      needWake = false;
  77.                                  }
  78.                              }
  79.                              msg.next = p;
  80.                              prev.next = msg;
  81.                          }
  82.                          if (needWake) {
  83.                              nativeWake(mPtr);
  84.                          }
  85.                      }
  86.                      return true;
  87.              }
  88.  // 之后,随着Looper对象的无限消息循环
  89.  // 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
  90.  // 最终回调Handler.handleMessage()处理消息
复制代码
post()


  • 不需外部创建消息对象,而是内部根据传入的Runnable对象 封装消息对象
  • 回调的消息处理方法是:复写Runnable对象的run()
  1. /**
  2.    * 源码分析:Handler.post(Runnable r)
  3.    * 定义:属于处理者类(Handler)中的方法
  4.    * 作用:定义UI操作、将Runnable对象封装成消息对象 & 发送 到消息队列中(Message ->> MessageQueue)
  5.    * 注:
  6.    *    a. 相比sendMessage(),post()最大的不同在于,更新的UI操作可直接在重写的run()中定义
  7.    *    b. 实际上,Runnable并无创建新线程,而是发送 消息 到消息队列中
  8.    */
  9.    public final boolean post(Runnable r)
  10.          {
  11.             return  sendMessageDelayed(getPostMessage(r), 0);
  12.             // getPostMessage(r) 的源码分析->>分析1
  13.             // sendMessageDelayed()的源码分析 ->>分析2
  14.          }
  15.                /**
  16.                 * 分析1:getPostMessage(r)
  17.                 * 作用:将传入的Runable对象封装成1个消息对象
  18.                 **/
  19.                private static Message getPostMessage(Runnable r) {
  20.                          // 1. 创建1个消息对象(Message)
  21.                          Message m = Message.obtain();
  22.                              // 注:创建Message对象可用关键字new 或 Message.obtain()
  23.                              // 建议:使用Message.obtain()创建,
  24.                              // 原因:因为Message内部维护了1个Message池,用于Message的复用,使用obtain()直接从池内获取,从而避免使用new重新分配内存
  25.  ​
  26.                          // 2. 将 Runable对象 赋值给消息对象(message)的callback属性
  27.                          m.callback = r;
  28.                          // 3. 返回该消息对象
  29.                          return m;
  30.                      } // 回到调用原处
  31.               /**
  32.                 * 分析2:sendMessageDelayed(msg, 0)
  33.                 * 作用:实际上,从此处开始,则类似方式1 = 将消息入队到消息队列,
  34.                 * 即 最终是调用MessageQueue.enqueueMessage()
  35.                 **/
  36.                 public final boolean sendMessageDelayed(Message msg, long delayMillis)
  37.                  {
  38.                      if (delayMillis < 0) {
  39.                          delayMillis = 0;
  40.                      }
  41.  ​
  42.                      return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  43.                      // 请看分析3
  44.                  }
  45.               /**
  46.                 * 分析3:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
  47.                 **/
  48.                 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  49.                          // 1. 获取对应的消息队列对象(MessageQueue)
  50.                          MessageQueue queue = mQueue;
  51.  ​
  52.                          // 2. 调用了enqueueMessage方法 ->>分析3
  53.                          return enqueueMessage(queue, msg, uptimeMillis);
  54.                      }
  55.               /**
  56.                 * 分析4:enqueueMessage(queue, msg, uptimeMillis)
  57.                 **/
  58.                  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  59.                       // 1. 将msg.target赋值为this
  60.                       // 即 :把 当前的Handler实例对象作为msg的target属性
  61.                       msg.target = this;
  62.                       // 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
  63.                       // 实际上则是将该消息派发给对应的Handler实例        
  64.  ​
  65.                      // 2. 调用消息队列的enqueueMessage()
  66.                      // 即:Handler发送的消息,最终是保存到消息队列
  67.                      return queue.enqueueMessage(msg, uptimeMillis);
  68.              }
  69.              // 注:实际上从分析2开始,源码 与 sendMessage(Message msg)发送方式相同
复制代码

参考连接:
Android异步通讯:手把手带你深入分析 Handler机制源码 - 简书 (jianshu.com)
Android Handler 源码解析,从入门到险些入门_android handler源码-CSDN博客

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表