Android组件Handler Looper Message理解

2024-03-19 10:08

本文主要是介绍Android组件Handler Looper Message理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android组件_Handler Looper Message理解

一、Handler机制概述

Handler机制是Android中一种消息处理机制。
主要组成或重要概念:
1. Message,线程间通讯的数据单元。
2. Message Queue,消息队列,用来存放Handler发布的消息,按照FIFO执行。
3. Handler是Message的主要处理者,负责将Message添加到消息队列意见对消息队列中的Message进行处理。
4. Looper循环器,循环去除Message Queue里面的Message,并交付给相应的Handler进行处理。
5. Thread UI thread通常就是main thread,Android启动程序时会替它建立一个Message Queue。
每一个线程里可以含有一个Looper对象以及MessageQueue数据结构。
6. ThreadLocal 他的作用是帮助Handler获得当前线程的Looper(多个线程可能有多个Looper)

二、使用场景

  1. 我们常常用Handler来更新UI,但是不是说Handler就是把用来更新UI的,耗时的I/O操作,读取文件,访问网络等等都是可以在Handler里面操作的。

  2. 子线程间通讯,可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

  3. 延时任务等

三、使用方法

3.1. 子线程线程里更新UI

public class MainActivity extends AppCompatActivity {//主线程中的handlerHandler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);//获得刚才发送的Message对象,然后在这里进行UI操作Log.e(TAG,"------------> msg.what = " + msg.what);...}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//在子线程中发送更新消息给主线程的handler去更新UInew Thread(new Runnable() {@Overridepublic void run() {Message m = mHandler.obtainMessage();...mHandler.sendMessage(m);}}).start();}
}

3.2 子线程间通信

        final Handler[] h = new Handler[1];new Thread(new Runnable() {@Overridepublic void run() {Looper.prepare();//创建子线程的looper,子线程使用handler消息传递,这一步必须要有,因为默认的子线程是没有Looper对象的h[0] = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.e(TAG,"------------> msg.what = " + msg.what);}};Looper.loop();//消息池消息循环处理}}, "work1").start();new Thread(new Runnable() {@Overridepublic void run() {Message msg = h[0].obtainMessage();//msg.sendToTarget();h[0].sendEmptyMessage(0);//在work2子线程中使用持有work1子线程looper的handler发送消息至work1中去做消息处理}}, "work2").start();

3.3 HandlerThread使用

    {MyHandlerThread mHandlerThread = new MyHandlerThread("work");Handler mHandler = new Handler(mHandlerThread.getLooper()){//将该mHandler与HandlerThread对象的Looper关联起来@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};}MyHandlerThread mHandlerThread = new MyHandlerThread("work");Handler mHandler = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};}

3.4 Handler CallBack参数使用
在构造Handler对象的时候可以穿入CallBack参数,如下

//主线程:Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {return false;}});
//其他线程中:
handler.sendMessageXXX(msg);

3.5 Handler post方法使用

    //主线程中创建mPostHandlerHandler mPostHandler = new Handler();//子线程中使用post方法new Thread(new Runnable() {@Overridepublic void run() {/* Message m = mHandler.obtainMessage();mHandler.sendMessage(m);*/mPostHandler.post(new Runnable() {@Overridepublic void run() {//更新UI ,注意这里虽然操作写在子线程中,事实运行时是在主线程中,原因分析在4.2.5中}});}}).start();

3.6 Handler 延时任务/循环定时操作

延时任务
new Handler().postDelayed(new Runnable(){   public void run() {  //show dialog}  }, 5000);  //延时5秒后执行runnable run方法
循环定时操作:
1,首先创建一个Handler对象  Handler handler=new Handler();  2,然后创建一个Runnable对
Runnable runnable=new Runnable(){  @Override  public void run() {  // TODO Auto-generated method stub  //要做的事情,这里再次调用此Runnable对象,以实现每两秒实现一次的定时器操作  handler.postDelayed(this, 2000);  }  
};  3,使用PostDelayed方法,两秒后调用此Runnable对象  
handler.postDelayed(runnable, 2000);  4,如果想要关闭此定时器,可以这样操作
handler.removeCallbacks(runnable); 

四、原理分析

Handler机制的铁三角-Handler、Looper和MessageQueue,另外还有下面进行浅入分析。
整体架构 :
handler_frame

4.1 主线程Handler对象与主线程Looper对象关联

对3.1中的例子进行浅入的分析。

4.1.1 Handler对象初始化获取主线程的Looper对象

1.主线程Handler对象初始化:

Handler mHandler = new Handler(){...}

默认的构造函数会将该Handler对象与当前线程的Looper对象关联。下面我们看下构造函数是如何关联上当前线程的looper的。
Handler的默认构造函数:

frameworks/base/core/java/android/os/Handler.javapublic Handler() {this(callback:null, async:false);}

2.如果该线程没有looper对象,该handler将抛异常:

    public Handler(Callback callback, boolean async) {...mLooper = Looper.myLooper();//if (mLooper == null) {//如果当前线程没有Looper对象将抛出异常throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}

3.sThreadLocal对象中保存着当前线程的looper对象,Looper.myLooper()即用来获取当前线程的Looper对象:

frameworks/base/core/java/android/os/Looper.javapublic static @Nullable Looper myLooper() {return sThreadLocal.get();//这里获取当前线程的Looper对象}
4.1.2 主线程初始化时创建Looper对象及MessageQueue对象

1.对于UI线程即我们所说的Main线程及ActivityThread,在ActivityThread创建的时候,main函数中创建了Looper对象,并通过Looper.loop()是主线程进入消息循环中:

frameworks/base/core/java/android/app/ActivityThread.javapublic static void main(String[] args) {...Looper.prepareMainLooper(); //创建主线程Looper对象ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}...Looper.loop();//不断循环处理MessageQueue中的消息throw new RuntimeException("Main thread loop unexpectedly exited");}

2.Looper.prepareMainLooper()创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环:

frameworks/base/core/java/android/os/Looper.javapublic static void prepareMainLooper() {prepare(false);//创建Looper对象,并且将新建的looper对象与当前线程关联synchronized (Looper.class) {if (sMainLooper != null) { //可以看出主线程应该只有一个Looper对象throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();//返回当前线程的Looper对象}}

3.Looper.prepare()函数为当前线程(此处为主线程,也可以是其他线程)创建Looper对象,并将该对象设置至线程的sThreadLocal变量中:

  private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) { // 说明prepare不能调用两次,否则会抛出异常,保证一个线程只有一个Looper实例throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));//sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。}

4.Looper.myLooper()返回当前线程的Looper对象,这里也就验证了myLooper()的确是从sThreadLocal变量中获取了当前线程的Looper对象。

    public static @Nullable Looper myLooper() {return sThreadLocal.get();}

概述一下就是,主线程在初始化时就生成了含有一个不可退出的MessageQueue的Looper对象,并将该Looper对象保存在主线程的sThreadLocal变量中;
当在主线程中创建Handler对象时,会将该Handler对象与当前线程的sThreadLocal变量中保存的主线程Looper对象关联起来。
因此,在上面3.1的例子中,主线程new一个Handler对象时会将该Handler对象与主线程中的Looper对象关联,执行Looper.loop()不断循环处理消息;

4.2 Handler、Looper、Message及HandlerThread浅析

4.2.1 Handler

1.可以使用的方法

post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageDelayed(Message,long)
sendMessageAtTime(Message,long) 

2.以上方法都最终都将调用sendMessageAtTime(),将消息加入msg的目标Handler对象关联的Looper持有的消息队列中,之后就是排队消息队列循环处理消息了:

    public boolean sendMessageAtTime(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关联的looper中的消息队列中}
4.2.2 Looper:

1.Looper构造函数,看到Looper的构造函数里新建了一个MessageQueue对象:

    private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);//创建消息队列mThread = Thread.currentThread();}

2.Looper.loop(),循环处理消息队列中消息的函数:

    /*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/public static void loop() {...// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();for (;;) {Message msg = queue.next(); // might block,若无消息则会阻塞在这里。...try {msg.target.dispatchMessage(msg); //调用发该message的handler的dispatchMessage方法;} finally {}...msg.recycleUnchecked();//处理完的msg可以进行回收,实现的地方清除了该msg对象的内容,并将其保留在消息池中,以供循环使用            }}

3.在dispatchMessage中进行消息处理,post方法中的runnable就是这里的msg.callback

frameworks/base/core/java/android/os/Handler.javapublic void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg); //执行post方法中的runnable方法} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) { //如果创建handler时有传入callback的话return;}}handleMessage(msg);//创建handler时需复写方法handleMessage(),处理消息}}

3.1首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg), handleCallback源码如下:

private static void handleCallback(Message message) {message.callback.run();
}

这样我们我们就清楚地看到我们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。
3.2.如果我们不是通过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null,代码继续往下执行,接着我们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,我们在上面提到过,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。
3.3.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。
综上,我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。

4.2.3 MessageQueue

1 MessageQueue的next()方法
在消息队列中不断取出消息,next方法是个无限循环的方法,如果有消息返回这条消息并从链表中移除,而没有消息则一直阻塞在这里。

Message next() {for (;;) {synchronized (this) {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) {...// Got a message.mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;msg.markInUse();return msg;}} }}
4.2.4 HandlerThread

1.自带Looper的Thread:

frameworks/base/core/java/android/os/HandlerThread.java
public class HandlerThread extends Thread {
@Overridepublic void run() {...Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}onLooperPrepared();Looper.loop();...}
}

我们看到HandlerThread继承自Thread,并且在run函数中使用Looper.prepare()为使用线程创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal);在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量;loop()开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
因此也可以使用HandlerThread,这样可以不用像普通线程那样需要Looper.prepare()和Looper.loop(),因为HandlerThread为我们做好了准备工作;

2.另外可以通过HandlerThread的getLooper方法获的该HandlerThread的Looper对象:

    public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}

handleMessage

4.2.5 Handler中post方法

3.5中的例子中子线程中post中的run方法运行在主线程中,原因分析如下:
1.传入Runnable对象

public final boolean post(Runnable r){return  sendMessageDelayed(getPostMessage(r), 0);}

2.这里将Runnable封装到一个Message对象中

  private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r; //将runnable传入message对象中的callback变量中return m;}

3.之后与普通的handler sendMessage方法流程一致,调用用sendMessageAtTime,将该消息加入该消息的目标handler,及mPostHandler关联的looper中的消息队列中,如何处理在4.2.1 中有提到过。

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

也就是说,虽然runnable复写方法在子线程中,但是同样是封装成message对象传入主线程的looper持有的MessageQueue对象中,在主线程中对封装了runnable变量的message进行消息执行处理,因此post中的run方法是运行在主线程中的。

五、注意事项

  1. Handler在处理消息需要严格区分是否是在UI线程中,Handler一般用在非UI线程中来传递消息,在非UI线程中使用Handler来发送消息,消息
    处理会被严格执行,但如果在UI线程中使用Handler来发送消息,相同的消息在内部会被合并,且执行时序也得不到保证
  2. 因为HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程,比较合适处理那些需要花费时间偏长的任务。我们只需要把任务发送给HandlerThread,然后就只需要等待任务执行结束的时候通知返回到主线程就好了。
  3. android中handler使用应该注意的由handler引起的OOM内存泄漏) http://blog.csdn.net/javazejian/article/details/50839443
  4. 在普通线程中使用机制时要记得先Looper.prepare(); 最后还要Looper.loop();

六、案例分析

以后补上

这篇关于Android组件Handler Looper Message理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/825622

相关文章

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分