Android多线程----异步消息处理机制之Handler

2024-09-01 22:38

本文主要是介绍Android多线程----异步消息处理机制之Handler,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、handler的引入:

我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了

 private Handler handler = new Handler() {
18         public void handleMessage(Message msg) {
19             switch (msg.what) {
20             case UPDATE_TEXT:
21                 text.setText("Nice to meet you");
22                 break;
23             default:
24                 break;
25             }
26         }
27     };new Thread(new Runnable() {
43                 @Override
44                 public void run() {
45                     Message message = new Message();
46                     message.what = UPDATE_TEXT;
47                     handler.sendMessage(message);
48                 }
49             }).start();

上方第45行代码,官方建议我们写成:(这样的话,可以由系统自己负责message的创建和销毁)

Message msg = handler.obtainMessage();

或者写成:

Message msg = Message.obtain();

上面的代码中,我们并没有在子线程中直接进行UI操作,而是创建了一个Message对象,并将它的what字段的值指定为了一个整形常量UPDATE_TEXT,用于表示更新TextView这个动作。然后调用Handler的sendMessage()方法将这条Message发送出去。很快,Handler就会收到这条Message,并在handleMessage()方法,在这里对具体的Message进行处理(需要注意的是,此时handleMessage()方法中的代码是在主线程中运行的)。如果发现Message的what字段的值等于UPDATE_TEXT,就将TextView显示的内容更新。运行程序后,点击按钮,TextView就会显示出更新的内容。

 注:如果从源码的角度理解,粗略的描述是这样的:

先是调用了handler的obtainMessage()方法得到Message对象。在obtainMessage()方法里做的事情是:调用了Message.obtain(this)方法,把handler作为对象传进来。在Message.obtain(this)方法里做的事情是:生成message对象,把handler作为参数赋值给message的target属性。总的来说,一个Handler对应一个Looper对象,一个Looper对应一个MessageQueue对象,使用Handler生成Message,所生成的Message对象的Target属性,就是该对象。而一个Handler可以生成多个Message,所以说,Handler和Message是一对多的关系。

Android官网总结的关于Handler类的两个主要用途:

(1)执行定时任务:

指定任务时间,在某个具体时间或某个时间段后执行特定的任务操作,例如使用Handler提供的postDelayed(Runnable r,long delayMillis)方法指定在多久后执行某项操作,比如当当、淘宝、京东和微信等手机客户端的开启界面功能,都是通过Handler定时任务来完成的。

我们接下来讲一下post。 

(2)线程间的通信:

在执行较为耗时的操作时,Handler负责将子线程中执行的操作的结果传递到UI线程,然后UI线程再根据传递过来的结果进行相关UI元素的更新。(上面已有说明)

// 声明一个Handler对象
14     private static Handler handler=new Handler();//使用post方式
// 新启动一个子线程
29                 new Thread(new Runnable() {                    
30                     @Override
31                     public void run() {
32                         // tvMessage.setText("...");
33                         // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问
34                         // 使用post方式修改UI组件tvMessage的Text属性
35                         handler.post(new Runnable() {                    
36                             @Override
37                             public void run() {
38                                 tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。");                        
39                             }
40                         });                                
41                     }
42                 }).start();//使用postDelayed方式
// 新启动一个子线程new Thread(new Runnable() {                    
51                     @Override
52                     public void run() {
53                         // 使用postDelayed方式修改UI组件tvMessage的Text属性值
54                         // 并且延迟3S执行
55                         handler.postDelayed(new Runnable() {
56                             
57                             @Override
58                             public void run() {
59                                 tvMessage.setText("使用Handler.postDelayed在工作线程中发送一段执行到消息队列中,在主线程中延迟3S执行。");    
60                                 
61                             }
62                         }, 3000);                        
63                     }
64                 }).start();
65                 
注意的是:对于Post方式而言,它其中Runnable对象的run()方法的代码(37行至39行或者58至61行),均运行在主线程上(虽然看上去是写在子线程当中的),如果我们在这段代码里打印日志输出线程的名字,会发现输出的是Main Thread的名字。所以对于这段代码而言,不能执行在UI线程上的操作,一样无法使用post方式执行,比如说访问网络。 

通过Handler实现线程间通信:

1、在Worker Thread发送消息,在MainThread中接收消息:

【实际意义】点击按钮时,程序访问服务器,服务器接到请求之后,会返回字符串结果,然后更新到程序。


package com.example.test0207_handler;import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends Activity {private TextView textView ; private Button button ;private Handler handler ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = (TextView)findViewById(R.id.TextViewId) ;button = (Button)findViewById(R.id.ButtonId) ;    handler = new MyHandler() ;button.setOnClickListener(new ButtonListener()) ;}//在MainAthread线程中接收数据,从而修改TextView的值class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {System.out.println("handleMessage--->"+Thread.currentThread().getName()) ;//得到当前线程的名字String s = (String)msg.obj ;textView.setText(s) ;}}//生成线程对象,让NetworkThread线程启动class ButtonListener implements OnClickListener {@Override        public void onClick(View arg0) {Thread t = new NetworkThread() ;t.start();}}//在Worker Thread线程中发送数据class NetworkThread extends Thread {@Override public void run(){System.out.println("network--->"+Thread.currentThread().getName()) ;//得到当前线程的名字//模拟访问网络:当线程运行时,首先休眠2秒钟try {Thread.sleep(2*1000) ;} catch (InterruptedException e) {e.printStackTrace();}//变量s的值,模拟从网络当中获取的数据String s = "从网络中获取的数据" ;//textView.setText(s) ; //这种做法是错误的,只有在Mainthread中才能操作UI            //开始发送消息Message msg = handler.obtainMessage() ;    msg.obj = s ;handler.sendMessage(msg) ;//sendMessage()方法,在主线程或者Worker Thread线程中发送,都是可以的,都可以被取到}}    @Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}

这段代码的结构,和最上面的第一章节是一样的。

上方代码中,我们在子线程中休眠2秒来模拟访问网络的操作。

65行:用字符串s表示从网络中获取的数据;70行:然后我们把这个字符串放在Message的obj属性当中发送出去,并在主线程中接收(36行)。

运行后结果如下:

点击按钮后结果如下:

点击按钮后,后台输出结果如下:

可以看到,子线程的名字是:Thread-1118,主线程的名字是:main。

2、在MainThread中发送消息,在Worker Thread中接收消息:

【实例】点击按钮,在在MainThread中发送消息,在Worker Thread中接收消息,并在后台打印输出。

package com.example.m03_handle01;import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {private Button button ;private Handler handler ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = (Button)findViewById(R.id.ButtonId) ;//当用户点击按钮时,发送Message的对象msgbutton.setOnClickListener(new OnClickListener() {  //使用匿名内部类为button绑定监听器@Overridepublic void onClick(View v) {Log.i("onClick:", Thread.currentThread().getName());Message msg = handler.obtainMessage() ;handler.sendMessage(msg) ;}            }) ;WorkerThread wt = new WorkerThread() ;wt.start() ;}//在WorkerThread生成handlerclass WorkerThread extends  Thread {@Overridepublic void run() {//准备Looper对象Looper.prepare() ;//在WorkerThread当中生成一个Handler对象handler = new Handler() {@Overridepublic void handleMessage(Message msg) {Log.i("handleMessage:", Thread.currentThread().getName());Log.i("后台输出", "收到了消息对象");}};//调用Looper的loop()方法之后,Looper对象将不断地从消息队列当中取出对象,然后调用handler的handleMessage()方法,处理该消息对象//如果消息队列中没有对象,则该线程阻塞Looper.loop() ;   //通过Looper对象将消息取出来}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}

上方的第42行至54行代码:这是MainThread中发送消息,在Worker Thread中接收消息的固定写法。上面的三个步骤再重复一下:

  • 准备Looper对象
  • 在WorkerThread当中生成一个Handler对象
  • 调用Looper的loop()方法之后,Looper对象将不断地从消息队列当中取出对象,然后调用handler的handleMessage()方法,处理该消息对象;如果消息队列中没有对象,则该线程阻塞

注意,此时handleMessage()方法是在Worker Thread中运行的。

运行程序后,当我们点击按钮,就会在后台输出“收到了消息对象”这句话:

小小地总结一下:

首先执行Looper的prepare()方法,这个方法有两个作用:一是生成Looper对象,而是把Looper对象和当前线程对象形成键值对(线程为键),存放在ThreadLocal当中,然后生成handler对象,调用Looper的myLooper()方法,得到与Handler所对应的Looper对象,这样的话,handler、looper 、消息队列就形成了一一对应的关系,然后执行上面的第三个步骤,即Looper在消息队列当中循环的取数据。

这篇关于Android多线程----异步消息处理机制之Handler的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1128243

相关文章

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

SpringKafka消息发布之KafkaTemplate与事务支持功能

《SpringKafka消息发布之KafkaTemplate与事务支持功能》通过本文介绍的基本用法、序列化选项、事务支持、错误处理和性能优化技术,开发者可以构建高效可靠的Kafka消息发布系统,事务支... 目录引言一、KafkaTemplate基础二、消息序列化三、事务支持机制四、错误处理与重试五、性能优

SpringIntegration消息路由之Router的条件路由与过滤功能

《SpringIntegration消息路由之Router的条件路由与过滤功能》本文详细介绍了Router的基础概念、条件路由实现、基于消息头的路由、动态路由与路由表、消息过滤与选择性路由以及错误处理... 目录引言一、Router基础概念二、条件路由实现三、基于消息头的路由四、动态路由与路由表五、消息过滤

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

Python实现自动化接收与处理手机验证码

《Python实现自动化接收与处理手机验证码》在移动互联网时代,短信验证码已成为身份验证、账号注册等环节的重要安全手段,本文将介绍如何利用Python实现验证码的自动接收,识别与转发,需要的可以参考下... 目录引言一、准备工作1.1 硬件与软件需求1.2 环境配置二、核心功能实现2.1 短信监听与获取2.

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda