当前位置: 首页 > news >正文

Handler 消息处理机制总结

7839d8ee6d0a13db148d5989043a5157.gif

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、Handler 简介
二、Handler 消息处理机制原理
三、Handler 机制处理的4个关键对象
四、 Handler常用方法
五、子线程更新UI 异常处理
六、主线程给子线程发送消息的方法
七、主线程发送消息给子线程的例子
八、子线程给主线程发送消息的方法
九、 主、子 线程 互发消息方法
十、子线程方法中调用主线程更新UI的方法
十一、移除Handler 发送的消息方法

一、Handler 简介

在了解Handler 之前,我们需要先了解Handler的继承关系 继承关系如下:

java.lang.Object↳    android.os.Handler

Handler是 Android中用来更新UI 的一套消息处理机制。Handler 允许线程间发送MessageRunnable对象进行通信。在Android中UI修改只能通过UI Thread,子线程不能更新UI。如果子线程想更新UI,需要通过 Handler发送消息给主线程,进而达到更新UI的目的。

二、Handler 消息处理机制原理

Android 应用程序创建的时候,系统会给每一个进程提供一个LooperLooper 是一个死循环,它内部维护一个消息队列,Looper不停的从消息队列中取Message,取到的消息就发送给handler,最后Handler 根据接收的消息去修改UI等。

三、Handler 机制处理的4个关键对象

1.Message

线程之间传递的消息,可以携带一些简单的数据供子线程与主线程进行交换数据。

2.Message Queue

存放通过Handler 发送的Message 的消息队列,每一个线程只有一个消息队列。

3.Handler

消息处理者,主要用于发送跟处理消息。

主要功能:
发送消息SendMessage()
处理消息 HandleMessage()

4.Looper

内部包含一个死循环的MessageQueue,用于存储handler发送的MessageLooper则是不断的从消息队列中取消,如果有消息就取出发送给Handler 处理,没有则阻塞。

5.总结:

Handler负责发送MessageMessage QueueLooper负责从Message Queue 遍历Message ,然后直接把遍历的消息回传给Handler自己,通过Handler自身的handleMessage处理更新UI等操作。

10316c99ab417250d9c01655703a9173.jpeg

主线程、子线程间通信简单流程

四、 Handler常用方法

1.Runnable对象

  • post(Runnable)

使用方法举例:

public void BtnRunnableMethod(View view) {// 1.Runnable 对象RunnableHandlderMethod();}/*** Runnable 对象更新 UI * **/private Handler mRunnableHandler = new Handler();public void RunnableHandlderMethod() {new Thread() {@Overridepublic void run() {try {Thread.sleep(1000);mRunnableHandler.post(new Runnable() {@Overridepublic void run() {((Button) findViewById(R.id.btn_runnable)).setText("Runnable");}});} catch (InterruptedException e) {e.printStackTrace();}}}.start();}
  • postAtTime(Runnable, long)

  • postDelayed(Runnable, long)

2. Message 对象

  • sendEmptyMessage(int)

使用方法举例:

public void BtnMessageThreadMethod(View view) {// 2.Message 对象new MessageHandlerThreadMethod("子线程不能更新UI").start();}/*** Message 对象举例* ***/private int mCount = 0;private Handler mMessageHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);((Button) findViewById(R.id.btn_thread)).setText("" + mCount);}};class MessageHandlerThreadMethod extends Thread {String mString;public MessageHandlerThreadMethod(String str) {mString = str;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(1000);} catch (Exception e) {}mCount++;mMessageHandler.sendEmptyMessage(0);}}}
  • sendMessage(Message)

使用方法举例:

public void BtnMessageObjMethod(View view) {HandlerMessageObjMethods();}/**** handler sendmessage 处理方法* **/private Handler mHandlerMessageObj = new Handler() {@Overridepublic void handleMessage(Message msg) {((Button) findViewById(R.id.btn_message)).setText("arg1:"+ msg.arg1 + "\n" + msg.obj);}};private void HandlerMessageObjMethods() {new Thread() {@Overridepublic void run() {try {Thread.sleep(1000);// Message message = new Message();Message message = mHandlerMessageObj.obtainMessage();message.arg1 = 100;Person person = new Person();person.name = "Lucy";person.age = 12;message.obj = person;mHandlerMessageObj.sendMessage(message);} catch (InterruptedException e) {e.printStackTrace();}}}.start();}class Person {public int age;public String name;public String toString() {return "Name=" + name + "\n Age=" + age;}}
  • sendMessageAtTime(Message, long),

  • sendMessageDelayed(Message, long)

3.接收、处理Message

  • handleMessage(Message)

使用方法举例:

private Handler mMessageHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);((Button) findViewById(R.id.btn_thread)).setText("" + mCount);}};

五、子线程更新UI 异常处理

子线程不能更新UI,如果在子线程中更新UI,会出现CalledFromWrongThreadException 异常。

  • CalledFromWrongThreadException

d71b5bbd9361bcf8bb47f05411c28eba.jpeg

CalledFromWrongThreadException 子线程不能更新UI

解决方法:

子线程通过Handler发送消息给主线程,让主线程处理消息,进而更新UI

六、主线程给子线程发送消息的方法

此例子中子线程通过Looper不断遍历主线程发送的消息,Looper使用方法如下:

1. 准备Looper 轮询器

Looper.prepare();

2. Handler 处理遍历消息

Handler mHandler = new Handler()

3. 遍历消息队列

Looper.loop();

4.Looper 使用方法如下:

// 自定义 Loop 线程 ---> 不停的处理主线程发的消息class ChildLooperThread extends Thread {@Overridepublic void run() {// 1.准备成为loop线程Looper.prepare();// 2.处理消息mMainHandler = new Handler() {// 处理消息public void handleMessage(Message msg) {super.handleMessage(msg);... ...}});}};// 3.Loop循环方法Looper.loop();}}

七、主线程发送消息给子线程的例子

1. 启动 子线程,并再启动后发送消息

public void BtnMainMessageMethod(View view) {// 点击主线程 按钮,启动子线程,并在子线程启动后发送消息Message msg = new Message();msg.obj = "主线程:这是我携带的信息";if (mMainHandler != null) {// 2.主线程发送消息mMainHandler.sendMessage(msg);} else {Toast.makeText(getApplicationContext(), "开启子线程轮询消息,请再次点击发送消息",Toast.LENGTH_SHORT).show();// 1.开启轮询线程,不断等待接收主线成消息new ChildLooperThread().start();}}

2. 子线程启动,不停的变量主线程发送的消息

private Handler mMainHandler;String mMainMessage;// 自定义 Loop 线程 ---> 不停的处理主线程发的消息class ChildLooperThread extends Thread {@Overridepublic void run() {// 1.准备成为loop线程Looper.prepare();// 2.处理消息mMainHandler = new Handler() {// 处理消息public void handleMessage(Message msg) {super.handleMessage(msg);mMainMessage = (String) msg.obj;Log.i("TAG", "子线程:从主线程中接受的消息为:\n" + mMainMessage);// 使用 runOnUiThread 在主线程中更新UIrunOnUiThread(new Runnable() {@Overridepublic void run() {((Button) findViewById(R.id.btn_main_message)).setText(mMainMessage);}});}};// 3.Loop循环方法Looper.loop();}}

八、子线程给主线程发送消息的方法

1.子线程发送消息给主线程方法

public void BtnChildMessageMethod(View view) {new Thread() {public void run() {while (mCount < 100) {mCount++;try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}/*** 利用handler 对象发送消息 Message msg=Message.obtain(); Message* msg=new Message(); 获取一个消息对象message* */Message msg = Message.obtain();// 消息标记msg.what = 1;// 传递整型值msg.obj="传递object数据"msg.arg1 = mCount;Log.i("TAG", "count 值=" + mCount);if (mhandler != null) {mhandler.sendMessage(msg);}}}}.start();}

2.主线程接收并处理消息的方法

// 定义一个handler 主线程 接收子线程发来的信息private Handler mhandler = new Handler() {// 處理消息的方法public void handleMessage(android.os.Message msg) {switch (msg.what) {case 1:int value = msg.arg1;Log.i("TAG", "value值=" + value);((Button) findViewById(R.id.btn_child_message)).setText("当前值="+ value);break;default:break;}}};

九、 主、子 线程 互发消息方法

主要实现主、子线程每隔1s中通信一次

  • 实现打印Log如下:

12dc3dab8b943ec1532543a0d7b3cf29.jpeg

主、子线程通信log信息

  • 实现方法如下:

1. 启动子线程并发送给主线程消息

public void BtnMainChildMessageMethod(View view) {// 创建 名称为currentThread 子线程HandlerThread mChildThread = new HandlerThread("ChildThread");mChildThread.start();mChildHandler = new Handler(mChildThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {Log.i("TAG", "主线程对我说:" + msg.obj);// 子线程携带的消息Message message = new Message();message.obj = Thread.currentThread() + "我是子线程,小样,让我听你的没门";// 向主线程发送消息mainhandler.sendMessageDelayed(message, 1000);}};// 主线成发送空消息,开启通信mainhandler.sendEmptyMessage(1);}

2.主线程接收并处理子线程发送的消息

// 创建主线程private Handler mainhandler = new Handler() {@Overridepublic void handleMessage(Message msg) {Log.i("TAG", "子线程对我说:" + msg.obj);// 主线成携带的消息内容Message message = new Message();message.obj = Thread.currentThread() + "我是主线程:小子你得听我的。";// 向子线程发送消息mChildHandler.sendMessageDelayed(message, 1000);}};

十、子线程方法中调用主线程更新UI的方法

1.Activity 中 可以使用 runOnUiThread(Runnable)

// 使用 runOnUiThread 在主线程中更新UIrunOnUiThread(new Runnable() {@Overridepublic void run() {((Button) findViewById(R.id.btn_main_message)).setText(mMainMessage);}});

2.子线程使用 Handler.post(Runnable)

mRunnableHandler.post(new Runnable() {@Overridepublic void run() {((Button) findViewById(R.id.btn_runnable)).setText("Runnable");}});

3.View.post()

((Button) findViewById(R.id.btn_runnable)).post(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub((Button) findViewById(R.id.btn_runnable)).setText("View.post()方法使用");}});

4.Handler.sendMessage(Message)

public void BtnMainMessageMethod(View view) {// 点击主线程 按钮,启动子线程,并在子线程启动后发送消息Message msg = new Message();msg.obj = "主线程:这是我携带的信息";if (mMainHandler != null) {// 2.主线程发送消息mMainHandler.sendMessage(msg);}
}

十一、移除Handler 发送的消息方法

1.移除 handler 发送的所有消息

private Handler mChildHandler;
mChildHandler.removeCallbacksAndMessages(null);

2.移除 指定消息

private Handler mainhandler;
mainhandler.removeMessages(what);

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除。同时感谢您的阅读,期待您的关注。

e198e05444edc985d568719fca662505.jpeg

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【ARM Coresight Trace 系列文章 2.3 -- 简单介绍 ITM 比 Uart 的优点】
  • 【Linux系列】known_hosts详解
  • 白盒测试-发送请求-引出MockMvc源码类
  • 通过网关将数据上传到两台eqmx服务器上
  • Linux企业级应用(一)构建企业级Linux应用平台:全面指南
  • 算法:外卖调度
  • Python自动化测试之selenium - 元素定位
  • 【人工智能】Transformers之Pipeline(九):物体检测(object-detection)
  • 企业信息化建设搞得好了叫系统工程,搞不好叫面子工程
  • 放飞孔明灯
  • MySql的默认隔离级别是什么?可以解决幻读问题吗?
  • 嵌入式八股文-网络编程、多线程和进程
  • 微服务保护-Sentinel
  • Java多线程练习(3)
  • zabbix看图表的时候标题是乱码
  • 《Java编程思想》读书笔记-对象导论
  • 【技术性】Search知识
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • hadoop集群管理系统搭建规划说明
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • JavaScript 基础知识 - 入门篇(一)
  • JavaScript设计模式与开发实践系列之策略模式
  • learning koa2.x
  • React中的“虫洞”——Context
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • uni-app项目数字滚动
  • vue-loader 源码解析系列之 selector
  • vue-router的history模式发布配置
  • 从setTimeout-setInterval看JS线程
  • 回顾2016
  • 如何设计一个微型分布式架构?
  • 小李飞刀:SQL题目刷起来!
  • 学习Vue.js的五个小例子
  • ​批处理文件中的errorlevel用法
  • #if和#ifdef区别
  • #Linux(make工具和makefile文件以及makefile语法)
  • $forceUpdate()函数
  • (Charles)如何抓取手机http的报文
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (二)JAVA使用POI操作excel
  • (二)springcloud实战之config配置中心
  • (回溯) LeetCode 46. 全排列
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (六)vue-router+UI组件库
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .a文件和.so文件
  • .NET Core 2.1路线图
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • .sh 的运行
  • @angular/cli项目构建--Dynamic.Form
  • @converter 只能用mysql吗_python-MySQLConverter对象没有mysql-connector属性’...
  • @NoArgsConstructor和@AllArgsConstructor,@Builder