安卓 Handler 机制学习

  • 一、一些基本概念
    • 1. Handler机制的目的:
    • 2. handler与thread
    • 3. handler的message处理
  • 二、Handler机制组成部分
    • 1. message
    • 2. Handler
    • 3. MessageQueue 消息队列
    • 4. Looper 循环器
  • 三、 Handler的工作流程
  • 四、Looper方法
    • 1. 构造函数
    • 2. prepareMainLooper()
      • 定义
      • 调用
    • 3. Looper.prepare()方法
    • 4. myLooer()方法
    • 5. Looper.loop()方法
  • 五、MessageQueue
    • 1. enqueueMessage
    • 2. next()
  • 六、Handler
    • 1. 构造函数
    • 2. 发送消息: sendMessage(msg)
    • 3. 发送消息:post(Runnable)
    • 4. sendMessageAtTime
    • 4. dispatchMessage
  • 七、使用示例
    • 1. 使用handler作延时处理
    • 2. 子线程更新UI

一、一些基本概念

1. Handler机制的目的:

  • 耗时操作在独立线程操作
  • 跨线程传递消息
  • 消息托管
  • 消息处理的异步

2. handler与thread

  • Handler 作用是实现异步消息处理,Thread是新线程
  • Handler 的调用者仍为同一线程
  • 安卓的UI操作要求必须在主线程执行,如果Handler里操作过于耗时,调用者会阻塞。安卓提供了几种方式在其它线程来操作UI,如: Activity.runOnUiThread(Runnable), View的Post 、AsnycTask类等,它们基本都采用了Handler。
  • Handler直接调用线程的run方法。

3. handler的message处理

handler里使用Looper来处理Message,消息的处理是会阻塞的。

二、Handler机制组成部分

1. message

在线程间传递的对象稍为消息。Message.obtain()可以用来创建对象,这时创建对象类似是一个对象池,减少每次都new对象的开销。
Message是一个单向列表。

2. Handler

handler是message的处理器,同时也负责消息的发送和移除。

  • 发送即时消息: handler.sendMessage(Message msg)
  • 发送延时消息: handler.sendMessageDelayed(Message msg, long time)
  • 处理消息:handleMessage(Message msg)
  • 移除还未处理消息: handler.removeMessages(int what)

3. MessageQueue 消息队列

每个线程会有一个MessageQueue,message按when排序存放在消息队列 。

4. Looper 循环器

一个死循环,用来循环取出MessageQueue里的Message,交给Handler处理。每个线程只会有一个Looper对象。
Looper对象会持有1个MessageQueue对象、1个Thread对象(表示当前线程)、1个sMainLooper(指向主线程中创建的Looper对象)、1个ThreadLocal对象(负责持有作为线程局部变量的Looper对象)。

所以Thread-Looper-MessageQueue 之间的关系是一对一的。

三、 Handler的工作流程

  1. 创建线程、运行线程 thread.start()
  2. 创建Looper、MessageQueue Looper.prepare();
  3. 启动Loop循环机制,监听MessageQueue Looper.loop();
  4. 发送Message handler.sendMessage(msg); 或 handler.post(runnable);
  5. Loop监听到新Message,取出处理。 Looper.loop()

四、Looper方法

1. 构造函数

private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();
}

构造Looper的时候会创建MessageQueue。

2. prepareMainLooper()

用来在主线程中创建Looper对象,一般APP框架调用这个方法,不能在其它线程调用。

定义

public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}// myLooper 用来返回当前线程中持有的Looper对象sMainLooper = myLooper();}}

调用

public final class ActivityThread {public static final void main(String[] args) {......Looper.prepareMainLooper();......ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {    sMainThreadHandler = thread.getHandler();}......Looper.loop();......}
}

3. Looper.prepare()方法

静态方法用来创建MessageQueue,可以根据参数控制Looper是否可退出。这里使用ThreadLocal对象,这样不同线程可以保存不同的Looper。

public final class Looper{static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();final MessageQueue mQueue;public static void prepare(){prepare(true);}public static void prepare(boolean quitAllowed){if(sThreadLocal.get()!=null){throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}private Looper(boolean quitAllowed){mQueue = ne MessageQueue(quitAllowed);}
}

4. myLooer()方法

用来获取当前线程中持有的线程局部Looper对象变量。

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

5. Looper.loop()方法

无限循环从MessageQueue中取出 Message对象。如果消息不为空就发送给Handler的dispatchMessage方法处理,如果为空会等待。

public static void loop() {final Looper me = myLooper();  //获取TLS存储的Looper对象,获取当前线程的Looper if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;  //获取Looper对象中的消息队列....for (;;) { //主线程开启无限循环模式Message msg = queue.next(); //获取队列中的下一条消息,可能会线程阻塞if (msg == null) { //没有消息,则退出循环,退出消息循环,那么你的程序也就可以退出了return;}....//分发Message,msg.target 是一个Handler对象,哪个Handler把这个Message发到队列里,//这个Message会持有这个Handler的引用,并放到自己的target变量中,这样就可以回调我们重写//的handler的handleMessage方法。msg.target.dispatchMessage(msg);........msg.recycleUnchecked();  //将Message回收到消息池,下次要用的时候不需要重新创建,obtain()就可以了。}
}

五、MessageQueue

MessageQueue是一种消息队列的数据结果,其中重要的方法是 enqueueMessage()和 next() 方法。

  • 创建主线程的时候,会默认创建一个Looper,同时创建一个MessageQueue,
  • 创建子线程时,不会自动创建Looper,也不会创建MessageQueue,在调用Looper.prepare()的时候才会创建该线程的MessageQueue。

1. enqueueMessage

enQueueMessage主要操作:

  • 插入消息到消息队列
  • 唤醒Looper中等待的线程
boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {//msg.target就是发送此消息的Handlerthrow new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {//表示此消息正在被使用throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {//表示此消息队列已放弃IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;//将延迟时间封装到msg内部Message p = mMessages;//消息队列的第一个元素boolean needWake;if (p == null || when == 0 || when < p.when) {//如果此队列中头部元素是null(空的队列,一般是第一次),//或者此消息不是延时的消息,则此消息需要被立即处理,//此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,//然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理.msg.next = p;mMessages = msg;needWake = mBlocked;} else {//如果此消息是延时的消息,则将其添加到队列中,//原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,//延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。//插入延时消息不需要唤醒Looper线程。needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {//唤醒线程nativeWake(mPtr);}}return true;}

2. next()

  • next()里的 nativePollOnce(ptr, nextPollTimeoutMillis); 会阻塞消息,等待新消息到来。
  • 如果拿到消息还没时间,会听故事希望nextPollTimeoutMillis赋值,线程阻塞,等到时间自动唤醒
Message next() {final long ptr = mPtr;if (ptr == 0) {//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {//正常取出消息//设置mBlocked = false代表目前没有阻塞mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;msg.markInUse();return msg;}} else {//没有消息,会一直阻塞,直到被唤醒nextPollTimeoutMillis = -1;}if (mQuitting) {dispose();return null;}//...//此处有省略IdleHandler相关代码//...pendingIdleHandlerCount = 0;nextPollTimeoutMillis = 0;}}

六、Handler

1. 构造函数

    public Handler(Callback callback) {this(callback, false);}public Handler(Callback callback, boolean async) {//.....此处省略n行代码......mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}

构造Handler的时候可以传入Callback,也可以不传。

2. 发送消息: sendMessage(msg)

sendMessage(msg)和post(Runnable)都会调用到sendMessageDelayed(Message msg, long delayMillis),最终调用 sendMessageAtTime方法。

3. 发送消息:post(Runnable)

这个时候消息会赋上callback属性值。

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

使用方式:

mHandler.post(new Runnable() {@Overridepublic void run() {//主线程UI更新}
});

4. sendMessageAtTime

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {// 这里把mQueue赋值给本地的Queue,传给enQueueMessage,// 而mQueue就是mLooper.mQueueMessageQueue 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);}

4. dispatchMessage

    public void dispatchMessage(Message msg) {if (msg.callback != null) {// 使用post(runnable)的时候handleCallback(msg);} else {// mCallback 是Handler的构造函数赋值if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

七、使用示例

1. 使用handler作延时处理

这里是向主线程添加延时消息。

       binding.fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {new Handler().postDelayed(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "延迟处理", Toast.LENGTH_SHORT).show();}}, 1000*5);}});

2. 子线程更新UI

    private void update(String text) {TextView textView = findViewById(R.id.count);textView.setText(text);}private final Handler handler = new Handler(Looper.myLooper()) {@Overridepublic void handleMessage(android.os.Message msg) {if (msg.what == 1) {Bundle bundle = msg.getData();update(bundle.getString("text"));}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);com.cn.test.databinding.ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());setSupportActionBar(binding.toolbar);binding.fab.setOnClickListener(view -> {Message message = new Message();Bundle bundle = new Bundle();bundle.putString("text", (new Date()).getTime() + "");message.what = 1;message.setData(bundle);handler.sendMessage(message);});}

安卓 Handler 机制学习相关推荐

  1. Android 系统(189)---Android Handler:这是一份 全面、详细的Handler机制 学习攻略

    Android Handler:这是一份 全面.详细的Handler机制 学习攻略 前言 在Android开发的多线程应用场景中,Handler机制十分常用 今天,我将献上一份 全面.详细的Handl ...

  2. 【安卓Handler】Handler消息机制

    [安卓通信机制]Handler 由于以后以后要从事安卓系统方面的工作,然而本人半个月前还是Android小白. 这几天学习了gityuan的Android操作系统架构, 按照自己喜欢的方式总结一下. ...

  3. 安卓 Handler 消息机制(总)

    注:本文参考于某公开课,如有侵权,请联系本人,会立即删除 注:仅用作自我学习记录,未有任何商业用途. 关于handler的由来 开始前的闲言碎语 我们都知道handler是安卓的消息传递机制,使用ha ...

  4. 安卓 Handler 消息机制之Message源码

    一 概述 1. Message是handler机制中消息传递的载体,主要用来规范化传输数据的格式. 2. 源码内容含几个部分: 2.1 操作数据相关:一些属性和操作属性的getter和setter方法 ...

  5. 安卓 Handler 消息机制之MessageQueue源码

    首先,MessageQueue是属于底层类且它依附于创建他的Looper,除Looper外其他类无法单独创建他,如果要使用他,只能从Looper出获得. 下面将从几方面分析: 1. 消息队列存储原理 ...

  6. 浅谈Android中的Handler机制

    Handler是Android中提供的一种异步回调机制,也可以理解为线程间的消息机制.为了避免ANR,我们通常会把一些耗时操作(比如:网络请求.I/O操作.复杂计算等)放到子线程中去执行,而当子线程需 ...

  7. Android Handler机制(一) 为什么设计Handler

    一. 前言 做安卓开发也有好几年了, 一直在用Hanlder, 都是为了做项目功能实现就完了,核心原理研究的不深,平时有什么问题都是看下博客也能够解决,积累的都是零散的,过段时间忘了又得重新去翻, A ...

  8. handler相关学习(三)handler必背

    问题一.Android-Handler同步屏障,消息机制之同步屏障 Android-Handler同步屏障 - 简书 Handler设置同步屏障之后可以拦截Looper对同步消息的获取和分发.加入同步 ...

  9. android面试 handler,Android面试之Handler相关学习

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? ###Android面试之Handler相关学习 1.Android消息机制之Looper.java源代码学习 1). ...

  10. 自己写个C++版本Handler来理解Android的Handler机制

    由于日常工作不需要经常写android上层应用,对Android的Handler机制一直处于模模糊糊的状态.使用Handler之后,回去写c++代码时,时刻怀念Android里面的Handler,希望 ...

最新文章

  1. 参数估计_数据分析|统计之一个总体参数估计(附代码)
  2. 云计算技术 — Kubernetes v.s. OpenShift
  3. 鞋带的超炫系法-真是门艺术[转载]
  4. An error has occurred while drawing:java.lang.IllegalStateException: The display list is not valid.
  5. “面试不败计划”: java语言基础面试题(一)
  6. SQL Server 系统存储过程
  7. 在Linux中创建静态库.a和动态库.so
  8. 移动端分享到微信和QQ
  9. 查询数据表中重复记录
  10. php 入库乱码,解决php 中文字符入库或显示乱码的简单示例
  11. 高级商务办公软件应用【9】
  12. 年轻人要对自己狠一点
  13. ps抠出图像的透明阴影
  14. c语言产生式系统动物识别系统,简单动物识别系统的知识表示实验报告
  15. 矩阵分析:广义逆矩阵,{1}逆,MP逆,D逆
  16. 图像处理基础——空间域处理之灰度变换
  17. Java实现LeetCode第199场周赛(题号5472,5473,5474,5462)
  18. 异常(Exceptation)
  19. Visdrone2019数据集.txt标签文件转换为voc格式.XML标签文件
  20. Python数据分析实战之营销组合模型

热门文章

  1. fxp连接失败_FlashFXP连接失败(连接超时)怎样解决
  2. java 时间乱码,Java乱码解决方案
  3. ID7S625高压逆变器驱动芯片
  4. VMware Workstation Server 服务器启动报1075错误
  5. 等保2.0|网络安全保护解决方案
  6. 倒立摆 adams matlab,基于ADAMS与MATLAB联合仿真地倒立摆设计毕业论文.docx
  7. 山东大学项目实训十五——开源万能解码框架解决
  8. 图片php木马制作教程,图片木马制作大法
  9. Red Hat 9.0下载及安装
  10. 一篇吃透前置加加和后置加加(附练习题)