Android多线程:Handler runOnUiThread 异步消息处理机制

devtools/2024/10/20 11:38:22/

目录

一,Android中的多线程问题

1.模拟耗时工作

2.Android开启子线程 

 二,在子线程中更新UI

Handler-toc" style="margin-left:40px;">1.异步消息处理机制 Handler

runOnUiThread%E6%9B%B4%E6%96%B0UI-toc" style="margin-left:40px;">2.使用runOnUiThread更新UI


一,Android中的多线程问题

        Android用户界面是与用户交互的接口,对于用户的操作,Android迅速响应用户输入(200ms内)是一个重要目标。因此,一些耗时操作(如:后台下载,异步加载图片等)需要放在子线程中运行,否则会导致主线程阻塞。

1.模拟耗时工作

        例如下面这段访问百度界面的代码,如果在主线程中运行的话就会出现android.os.Network-OnMainThreadException的报错,也就是在主线程中请求了网络操作,这是一种耗时操作。为了解决这个问题,就需要把操作放在子线程中运行。

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViews();setListeners();
}
private void setListeners() {btn_baidu.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {try {//获取百度链接URL url = new URL("https://www.baidu.com/");//获取输入流InputStream inputStream = url.openStream();byte[] bytes = new byte[1024];//存储输入的信息StringBuffer buffer = new StringBuffer();while((inputStream.read(bytes)) != -1){String str = new String(bytes, 0, bytes.length);buffer.append(str);}Log.i("baidu", buffer.toString());//关闭流inputStream.close();} catch (MalformedURLException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}});
}

2.Android开启子线程 

         在Android中开启线程的操作与在Java中一致,继承Thread类或实现Runnable接口,不了解的话可以阅读博客:Java线程基础:Thread Runnable 多线程 Synchronized 死锁...-CSDN博客。

例如下面用实现Runnable接口的方法来开启子线程,访问百度:

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViews();setListeners();
}
private void setListeners() {btn_baidu.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {new Thread(new Runnable() {@Overridepublic void run() {try {//获取百度链接URL url = new URL("https://www.baidu.com/");//获取输入流InputStream inputStream = url.openStream();byte[] bytes = new byte[1024];//存储输入的信息StringBuffer buffer = new StringBuffer();while((inputStream.read(bytes)) != -1){String str = new String(bytes, 0, bytes.length);buffer.append(str);}//在子线程中更改Ui界面,使用runOnUiThreadLog.i("baidu", buffer.toString());//关闭流inputStream.close();} catch (MalformedURLException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}}).start();}});
}

运行并查看日志,可以发现成功访问: 

 二,在子线程中更新UI

        使用子线程解决异步执行又会带来新问题,那就是在Android中,只有UI线程(也叫主线程)可以更新UI界面,子线程不能更新。为了在子线程中更新UI,我们需要使用Android异步消息处理机制

Handler">1.异步消息处理机制 Handler

        Android中的异步消息处理主要由4个部分组成:Message,Handler,MessageQueue,Looper。

  1. Message:在线程之间传递的消息,Message中可以封装一些数据如:what(int型,表示Message的编号),obj(封装的Object对象),此外还有int型的arg1,arg2等;
  2. Handler:用于在线程间发送和处理消息,发送消息使用sendMessage()方法,处理消息使用handleMessage()方法;
  3. MessageQueue:消息队列,用于存放Handler发送的消息,这些消息直到被处理前,会一直存放在消息队列中。每个线程只会有一个MessageQueue对象;
  4. Looper:Looper是每个线程中MessageQueue的管家,调用Looper的loop方法后,会进入无限循环,每当发现MessageQueue中存在一条消息,就会将其取出,并传递到Handler的handleMessage()方法中,每个线程只会有一个Looper对象; 

 异步消息处理机制的基本流程为:

(1)首先在主线程中创建一个Handler对象,并重写handleMessage方法。

(2)当子线程需要更改UI时,就创建一个Message对象,并通过Handler将Message发送出去,Message消息会被添加到MessageQueue中等待处理,Looper会一直尝试从消息队列中取出消息,并传给Handler的handleMessage方法。

(3)Handler的构造器中我们传入了Looper.getMainLooper,所以handleMessage方法中的代码会在UI线程中运行,我们就可以放心地进行UI操作。

下面是代码实例(获取网络图片):

private void getImg() {//1.在主线程中创建一个Handler对象,并重写handleMessage方法。Handler handler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {switch (msg.what){case 114514:Bitmap bitmap = (Bitmap) msg.obj;iv_img.setImageBitmap(bitmap);Log.i("114514", "获取图片成功!");break;}}};//设置监听btn_getimg.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {new Thread(new Runnable() {@Overridepublic void run() {try {//2.当子线程需要更改UI时,就创建一个Message对象URL url = new URL("https://profile-avatar.csdnimg.cn/8e4c56733fdd4dda90854384976d4bb0_ih_lzh.jpg!1");InputStream inputStream = url.openStream();Bitmap bitmap = BitmapFactory.decodeStream(inputStream);Message msg = handler.obtainMessage();//封装bitmap对象和设置对象编号msg.obj = bitmap;msg.what = 114514;//3.通过Handler将Message发送出去handler.sendMessage(msg);inputStream.close();} catch (MalformedURLException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}}).start();}});
}

runOnUiThread%E6%9B%B4%E6%96%B0UI">2.使用runOnUiThread更新UI

        runOnUiThread,在UI线程上运行指定的操作。如果当前线程是UI线程,则执行操作,如果当前线程不是UI线程,操作将被提交到UI线程的消息队列MessageQueue中。runOnUiThread只能在Activity中使用。

public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) {mHandler.post(action);//提交到消息队列} else {action.run();//操作执行}}

还是上面获取图片的例子,将Handler改为使用runOnUiThread更改UI:

private void getImg() {//设置监听btn_getimg.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {new Thread(new Runnable() {@Overridepublic void run() {try {URL url = new URL("https://profile-avatar.csdnimg.cn/8e4c56733fdd4dda90854384976d4bb0_ih_lzh.jpg!1");InputStream inputStream = url.openStream();Bitmap bitmap = BitmapFactory.decodeStream(inputStream);runOnUiThread(new Runnable() {@Overridepublic void run() {iv_img.setImageBitmap(bitmap);}});} catch (MalformedURLException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}}).start();}});
}

http://www.ppmy.cn/devtools/5062.html

相关文章

C++11 新特性:正则表达式

正则表达式 正则表达式(Regular Expressions)是一种强大的文本处理工具,它使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。正则表达式广泛应用于字符串的搜索、替换、验证等操作中,其灵活性和强大的表达能力使其成为…

进程上下文切换和线程上下文切换

进程上下文切换和线程上下文切换是操作系统中两个重要的概念,它们涉及到CPU在执行进程或线程时的上下文管理。 进程上下文切换是指在CPU执行一个进程时,需要保存当前进程的执行状态(上下文),然后加载另一个进程的上下…

Flink SQL

文章目录 一、Flink SQL1、sql-client准备1.1 基于yarn-session模式1.2 常用配置 2、流处理中的表2.1 动态表和持续查询2.2 将流转换成动态表2.3 用SQL持续查询2.4 将动态表转换为流 3、时间属性3.1 事件时间3.2 处理时间 4、DDL(Data Definition Language&#xff…

MySQL Explan执行计划详解

Explan执行计划 首先我们采用explan执行计划 执行一条sql,发现返回了12个列,下面会详细解释每一列 1、ID列 id列的值是代表了select语句执行顺序,是和select相关联的;id列的值大的会优先执行,如果id列为空最后执行&a…

HAP官方示范应用大更新:开箱即用,全面整合新功能

前言 为响应广大用户需求,明道云应用库最新上架了一系列示范性模板应用。这些模板应用大多都做了通用化设计,用户只需略微修改即可投入使用,节省了从零开始搭建的工作量,大大降低上手使用门槛。 此外,鉴于我们的应用…

【iOS】—— SDWebImage源码学习(2)(源码解读)

【iOS】—— SDWebImage源码学习(2)(源码解读) 1.UIKit层取消当前正在进行的异步下载占位图策略判断url是否合法下载图片操作 2. SDWebImageManager判断url是否合法判断已加载失败的url保存操作查找缓存 3. SDWebImageDownloaderS…

Reka团队打造前沿多模态语言模型,展现卓越性能

eka,一家新兴的人工智能公司,近期推出了一系列强大的多模态语言模型 - Reka Core、Reka Flash和Reka Edge。这些模型不仅能处理和推理文本,还能够灵活应对图像、视频和音频等多种输入,在各项测试中表现出色,在某些指标…

[Linux] haproxy地址映射实现性能测试

背景 需要虚拟1万个网元做性能测试,前提是已经有网元模拟器,可以在服务器上批量起虚拟IP, 每个虚拟IP对应一个网元进程,但是一台服务器的CPU数量有限,不可能起1万个模拟器进程。于是结合haproxy,将frontend的1万个IP映…