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

目录

一,Android中的多线程问题

1.模拟耗时工作

2.Android开启子线程 

 二,在子线程中更新UI

1.异步消息处理机制 Handler

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() {
        @Override
        public 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() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public 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界面,使用runOnUiThread
                        Log.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异步消息处理机制

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()){
        @Override
        public 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() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public 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();
        }
    });
}

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() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public 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() {
                            @Override
                            public void run() {
                                iv_img.setImageBitmap(bitmap);
                            }
                        });
                    } catch (MalformedURLException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    });
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YOLO-World: Real-Time Open-Vocabulary Object Detection 简介+安装+运行+训练(持续更新)

前言 YOLO_WORLD太牛了!!众所周知,传统是视觉目标检测一旦训练好后,如果我们需要增加新的识别目标的话,必须得重新训练模型。在生产中如果经常要新增检测目标,对时效性影响很大,而且随着数据量…

4G/5G布控球/移动执法仪/智能单兵电力巡检远程视频智能监控方案

一、背景与需求 随着科技的不断进步,视频监控技术已成为电力行业不可或缺的一环。电力行业的巡检及建设工作,因施工现场在人迹罕见的野外或山区,地形复杂多变,安全更是重中之重,现场工作的视频图像需实时传回监管中心…

公司电脑可以监控上网记录吗

电脑和网络已成为企业日常运营不可或缺的工具。然而,这也带来了一系列的安全和管理挑战。 特别是在员工上网行为方面,如何确保工作效率、信息安全和合规性成为了企业关注的重要问题。 在这样的背景下,许多企业考虑使用上网行为监控软件来管理…

cesium加载高层级离线影像地图瓦片(天地图、19级Arcgis)

实际加载效果如图: 1、下载离线地图瓦片方式(多种任选其一,个人倾向于Qgis工具): 方式1、采用第三方下载工具如:91卫图、水经注、全能电子地图下载器、bigemap等等。(这些有的下载层级不够&…

C#通用类库封装实战

数据库查询 特性方式获取数据库列的别名 数据库更新 使用简单工厂配置的方式

浅析MySQL 8忘记密码处理方式

对MySQL有研究的读者,可能会发现MySQL更新很快,在安装方式上,MySQL提供了两种经典安装方式:解压式和一键式,虽然是两种安装方式,但我更提倡选择解压式安装,不仅快,还干净。在操作系统…

YOLOV9目标检测-训练、验证、推理

目录 一、模型介绍 1.1摘要 1.2模型概要 1.2.1Programmable Gradient Information (1)Auxiliary Reversible Branch (2)Multi-level Auxiliary Information 1.2.2Generalized ELAN 二、环境配置 三、数据集准备 四、预训练权重下载 五、训练 六、模型评估 ​七、模…

图论:一文教你读懂常见的图遍历算法

一文教你读懂常见的图遍历算法 深度优先搜索(DFS): 从一个起始节点开始,访问该节点并将其标记为已访问。递归地访问所有与当前节点直接相连且未被访问过的节点。重复上述步骤,直到所有节点都被访问过或没有未访问的节…

【分享 网络墙测试】检测当前网络是否能用于其他平台,速度检测

文章日期:2024.04.17 类型:软件分享 兼容:win10 / win11 文章全程已做去敏处理!!! 【需要做的可联系我】 AES解密处理(直接解密即可)(crypto-js.js 标准算法&#xff09…

为什么科拓停车选择OceanBase来构建智慧停车SaaS应用

本文来自OceanBase的客户——拓客停车的实践分享 科拓停车简介与业务背景 作为智慧停车行业的佼佼者,科拓停车致力于提供全方位的智慧停车解决方案。服务涵盖车场运营管理、互联网智慧停车平台以及停车场增值服务等。通过不断研发创新,打造出了多样化的…

国内最具有影响力的三个 3D 视觉方向平台!

3D视觉工坊 我的朋友创办的「3D视觉工坊」公众号,由多名名校硕博士和大厂算法工程师共同运营,博主及合伙人参与研发过多种3D视觉产品,包括割草机、自动驾驶、工业3D相机等,有着非常丰富的落地经验。主要专注于3D高斯、工业3D视觉…

【Excel2LaTeX】复杂表格制作的解决方案

刚开始用LaTeX写论文,遇到的第一道坎就是绘制表格,较小的普通表格可以通过简单的语法实现,但是较大的复杂的表格却让我无从下手。 Excel2LaTeX插件 这里介绍一种我用到非常顺手的工具:Excel2LaTeX插件,下载地址&#x…

SSH协议的优缺点

SSH(Secure Shell)是一种用于在计算机网络上进行安全远程访问和执行命令的协议。提供加密通信通道,防止敏感信息在传输过程中被窃听或篡改。SSH还支持文件传输和端口转发等功能,使其成为广泛使用的安全远程管理工具。 1. 安全远程…

对桥接模式的理解

目录 一、背景二、桥接模式的demo1、类型A(形状类型)2、类型B(颜色类型)3、需求:类型A要使用类型B(如:红色的方形)4、Spring的方式 一、背景 在《对装饰器模式的理解》中&#xff0…

MySQL 基础使用

文章目录 一、Navicat 工具链接 Mysql二、数据库的使用1.常用数据类型2. 建表 create3. 删表 drop4. insert 插入数据5. select 查询数据6. update 修改数据7. delete 删除记录truncate table 删除数据 三、字段约束字段1. 主键 自增delete和truncate自增长字段的影响 2. 非空…

CS学习(九)—— 分支实现

if-else 18&#xff1a;若y<x&#xff0c;跳转L2 22&#xff1a;否则&#xff0c;跳转L3。 goto 可见&#xff0c;与if-else类似。但是用goto很low。 条件表达式 又是与if类似&#xff0c;那有没有区别&#xff1f; 当然&#xff0c;条件表达式两个式子都会计算&…

html、css、京东移动端静态页面,资源免费分享,可作为参考,提供InsCode在线运行演示

CSDN将我上传的免费资源私自变成VIP专享资源&#xff0c;且作为作者的我不可修改为免费资源&#xff0c;不可删除&#xff0c;寻找客服无果&#xff0c;很愤怒&#xff0c;&#xff08;我发布免费资源就是希望大家能免费一起用、一起学习&#xff09;&#xff0c;接下来继续寻找…

Leetcode 15. 三数之和(暴力->双指针)

给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例 1…

git clone报错:error invalid path ‘dorisdockerthirdpartiesdocker-composexxxx‘

git clone报错&#xff1a;error: invalid path ‘doris/docker/thirdparties/docker-compose/xxxx’ 在周日晚上&#xff0c;我尝试从GitHub上克隆Doris的代码库&#xff0c;以便进行学习。在使用IntelliJ IDEA进行克隆时&#xff0c;我遇到了一个Git错误。具体操作如下&…

UbuntuServer22.04安装docker

通过ubuntuserver安装docker是搭建开发环境最便捷的方式之一。下面介绍一下再ubuntu22.04上如何安装docker。相关内容参考官网链接&#xff1a;Install Docker Engine on Ubuntu 根据官网推荐&#xff0c;利用apt命令的方式安装&#xff0c;首先需要设置docker仓库&#xff0c…