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

《Android 应用开发基础教程》——第十四章:Android 多线程编程与异步任务机制(Handler、AsyncTask、线程池等)

目录

第十四章:Android 多线程编程与异步任务机制(Handler、AsyncTask、线程池等)

🔸 14.1 为什么需要多线程?

🔸 14.2 Handler + Thread 模型

✦ 使用 Handler 与 Thread 进行线程通信

✦ 简要说明:

🔸 14.3 Handler 消息机制详解

🔸 14.4 AsyncTask(适用于早期项目)

🔸 14.5 使用 Java 原生线程池(ThreadPoolExecutor)

✦ 优点:

🔸 14.6 Kotlin 开发推荐:协程 Coroutines(了解即可)

🔸 14.7 主线程中更新 UI 的常见方式总结

✅ 总结

小Demo(原来的习题板块)

项目结构

1. activity_main.xml

2. MainActivity.java

3. AndroidManifest.xml

代码说明

1. Handler + Thread 示例

2. AsyncTask 示例

3. 线程池示例

运行效果

总结


第十四章:Android 多线程编程与异步任务机制(Handler、AsyncTask、线程池等)


        Android 应用运行在单一的 UI 主线程(也叫 主线程 / UI Thread)上。为了避免阻塞界面、提升用户体验,我们需要将耗时操作(如网络请求、数据库访问、文件读写等)放到子线程中执行。本章将系统介绍 Android 中的多线程处理机制,包括 Handler、AsyncTask(已弃用但仍有参考意义)与线程池。


🔸 14.1 为什么需要多线程?

        在 Android 中,所有的 UI 更新必须在主线程中执行。如下操作若直接在主线程执行,可能导致 ANR(应用无响应):

  • 网络请求

  • 数据库操作

  • 大文件处理

  • 解码操作(如 Bitmap)


🔸 14.2 Handler + Thread 模型

✦ 使用 Handler 与 Thread 进行线程通信

Handler handler = new Handler(Looper.getMainLooper());new Thread(() -> {// 耗时操作(如模拟网络请求)try { Thread.sleep(2000); } catch (InterruptedException e) { }// 回到主线程更新 UIhandler.post(() -> {textView.setText("任务完成");});
}).start();

✦ 简要说明:

  • Thread:负责在子线程中执行耗时任务

  • Handler.post():用于将 UI 更新任务发送回主线程执行


🔸 14.3 Handler 消息机制详解

Handler handler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {textView.setText("收到消息:" + msg.obj.toString());}}
};new Thread(() -> {Message message = handler.obtainMessage();message.what = 1;message.obj = "数据加载完成";handler.sendMessage(message);
}).start();


🔸 14.4 AsyncTask(适用于早期项目)

⚠️ 从 Android 11 开始已被官方标记为 deprecated,但仍可用于了解异步结构设计思路。

 
private class MyTask extends AsyncTask<Void, Void, String> {@Overrideprotected void onPreExecute() {// 主线程:任务开始前(初始化 UI)progressBar.setVisibility(View.VISIBLE);}@Overrideprotected String doInBackground(Void... voids) {// 子线程:执行耗时操作return "任务结果";}@Overrideprotected void onPostExecute(String result) {// 主线程:任务完成后(更新 UI)progressBar.setVisibility(View.GONE);textView.setText(result);}
}// 调用方式:
new MyTask().execute();


🔸 14.5 使用 Java 原生线程池(ThreadPoolExecutor)

ExecutorService executor = Executors.newFixedThreadPool(4);executor.execute(() -> {// 执行耗时任务String data = loadData();// 回主线程更新 UIrunOnUiThread(() -> textView.setText(data));
});

✦ 优点:

  • 避免频繁创建销毁线程

  • 支持并发执行多个任务

  • 适合批量任务,如多个图片下载


🔸 14.6 Kotlin 开发推荐:协程 Coroutines(了解即可)

虽然本书采用 Java 编写,但需要了解,Kotlin 开发中主流推荐使用协程 (CoroutineScope, launch, suspend 等),比 AsyncTask 和 Handler 更轻便灵活。


🔸 14.7 主线程中更新 UI 的常见方式总结

场景方法
Thread → 主线程Handler.post()runOnUiThread()
AsyncTask → 主线程onPostExecute()
ExecutorService → 主线程runOnUiThread()

✅ 总结

  • Android 中的 UI 必须在主线程更新,耗时任务需放入子线程执行

  • Handler 是 Android 最基础的线程通信工具

  • AsyncTask 封装了常见的异步模式(但已被弃用)

  • Executor 提供了线程池管理机制,适合并发任务

  • Kotlin 推荐使用协程,Java 可用线程池 + Handler 配合实现异步任务


📢 下一章预告:

第十五章:Android 动画机制详解(属性动画、帧动画、过渡动画)


小Demo(原来的习题板块)

项目结构

MainActivity.java
activity_main.xml
AndroidManifest.xml

1. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="点击按钮开始任务"android:textSize="18sp"android:gravity="center" /><ProgressBarandroid:id="@+id/progressBar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="gone" /><Buttonandroid:id="@+id/btnHandler"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Handler + Thread 示例" /><Buttonandroid:id="@+id/btnAsyncTask"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="AsyncTask 示例" /><Buttonandroid:id="@+id/btnThreadPool"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="线程池示例" />
</LinearLayout>

2. MainActivity.java

package com.example.demo;import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class MainActivity extends AppCompatActivity {private TextView textView;private ProgressBar progressBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.textView);progressBar = findViewById(R.id.progressBar);Button btnHandler = findViewById(R.id.btnHandler);Button btnAsyncTask = findViewById(R.id.btnAsyncTask);Button btnThreadPool = findViewById(R.id.btnThreadPool);// Handler + Thread 示例btnHandler.setOnClickListener(v -> startHandlerThreadExample());// AsyncTask 示例btnAsyncTask.setOnClickListener(v -> startAsyncTaskExample());// 线程池示例btnThreadPool.setOnClickListener(v -> startThreadPoolExample());}// Handler + Thread 示例private void startHandlerThreadExample() {progressBar.setVisibility(ProgressBar.VISIBLE);Handler handler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {String result = (String) msg.obj;textView.setText(result);progressBar.setVisibility(ProgressBar.GONE);}}};new Thread(() -> {try {// 模拟耗时操作Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 发送消息到主线程Message message = handler.obtainMessage();message.what = 1;message.obj = "Handler + Thread 示例完成";handler.sendMessage(message);}).start();}// AsyncTask 示例private void startAsyncTaskExample() {new MyTask().execute();}private class MyTask extends AsyncTask<Void, Void, String> {@Overrideprotected void onPreExecute() {super.onPreExecute();progressBar.setVisibility(ProgressBar.VISIBLE);}@Overrideprotected String doInBackground(Void... voids) {try {// 模拟耗时操作Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return "AsyncTask 示例完成";}@Overrideprotected void onPostExecute(String result) {super.onPostExecute(result);textView.setText(result);progressBar.setVisibility(ProgressBar.GONE);}}// 线程池示例private void startThreadPoolExample() {progressBar.setVisibility(ProgressBar.VISIBLE);ExecutorService executor = Executors.newFixedThreadPool(4);executor.execute(() -> {try {// 模拟耗时操作Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 回到主线程更新 UIrunOnUiThread(() -> {textView.setText("线程池示例完成");progressBar.setVisibility(ProgressBar.GONE);});});}
}

3. AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.demo"><applicationandroid:allowBackup="true"android:label="多线程与异步任务示例"android:theme="@style/Theme.AppCompat.Light.DarkActionBar"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>

代码说明

1. Handler + Thread 示例
  • 使用 Handler 和 Thread 进行线程通信。
  • 子线程完成耗时任务后,通过 Handler 将结果发送回主线程更新 UI。
2. AsyncTask 示例
  • 使用 AsyncTask 执行耗时操作:
    • onPreExecute():在主线程中运行,用于初始化 UI。
    • doInBackground():在子线程中运行,执行耗时任务。
    • onPostExecute():在主线程中运行,用于更新 UI。
3. 线程池示例
  • 使用 ExecutorService 创建固定大小的线程池。
  • 子线程完成任务后,通过 runOnUiThread() 更新主线程 UI。

运行效果

  1. Handler + Thread 示例

    • 点击按钮后,进度条显示,2 秒后更新文本为“Handler + Thread 示例完成”。
  2. AsyncTask 示例

    • 点击按钮后,进度条显示,2 秒后更新文本为“AsyncTask 示例完成”。
  3. 线程池示例

    • 点击按钮后,进度条显示,2 秒后更新文本为“线程池示例完成”。

总结

  1. Handler 是线程间通信的基础工具,适合简单的线程交互。
  2. AsyncTask 已被弃用,但在旧项目中仍常见,适合轻量级异步任务。
  3. 线程池 提供更高效的线程管理,适合复杂的多线程场景。
  4. 在现代开发中,推荐使用 Kotlin 的协程(Coroutines)或 RxJava 来处理异步任务。

相关文章:

《Android 应用开发基础教程》——第十四章:Android 多线程编程与异步任务机制(Handler、AsyncTask、线程池等)

目录 第十四章&#xff1a;Android 多线程编程与异步任务机制&#xff08;Handler、AsyncTask、线程池等&#xff09; &#x1f538; 14.1 为什么需要多线程&#xff1f; &#x1f538; 14.2 Handler Thread 模型 ✦ 使用 Handler 与 Thread 进行线程通信 ✦ 简要说明&am…...

Apache 高级配置实战:从连接保持到日志分析的完整指南

Apache 高级配置实战&#xff1a;从连接保持到日志分析的完整指南 前言 最近在深入学习 Apache 服务器配置时&#xff0c;发现很多朋友对 Apache 的高级功能还不够了解。作为一个在运维路上摸爬滚打的技术人&#xff0c;我想把这些实用的配置技巧分享给大家。今天这篇文章会带…...

开源 OIDC(OpenID Connect)身份提供方(IdP)、iam选型

文章目录 开源 OIDC(OpenID Connect)身份提供方(IdP)、iam选型主流开源 OIDC(OpenID Connect)身份提供方(IdP)zitadeldexory开源 OIDC(OpenID Connect)身份提供方(IdP)、iam选型 主流开源 OIDC(OpenID Connect)身份提供方(IdP) 当前主流的**开源 OIDC(OpenI…...

Android OkHttp控制链:深入理解网络请求的流程管理

OkHttp作为Android和Java平台上广泛使用的HTTP客户端&#xff0c;其核心设计之一就是"控制链"(Chain)机制。本文将深入探讨OkHttp控制链的工作原理、实现细节以及如何利用这一机制进行高级定制。 一、什么是OkHttp控制链 OkHttp控制链是一种责任链模式的实现&#…...

【JVM 01-引言入门篇】

JVM 引言篇01 笔记记录 1. 什么是JVM&#xff1f;2. 学习JVM有什么用&#xff1f;3. 常见的JVM4. 学习路线 学习资料来源-b站黑马 1. 什么是JVM&#xff1f; 定义&#xff1a;Java虚拟机&#xff08;Java Virtual Machine 简称JVM&#xff09;是运行所有Java程序的抽象计算机&a…...

Pandas数据规整

&#xff08;1&#xff09;层次化索引 1.创建带层次化索引的df 第一种&#xff0c;直接创建 import pandas as pd import numpy as npdata pd.Series(np.random.randn(9),index [[a, a, a, b, b, c, c, d, d],[1, 2, 3, 1, 3, 1, 2, 2, 3]]) print(data) # a 1 -0.6416…...

ThreadLocal线程本地变量在dubbo服务使用时候遇到的一个坑

我昨天遇到一个问题&#xff0c;就是我springboot项目里面有一个提供代办服务审核的dubbo接口&#xff0c;这个接口给房源项目调用&#xff0c;但是碰到一个问题就是&#xff0c;房源项目每天凌晨5点会查询满足条件过期的数据&#xff0c;然后调用我这边的代办审核dubbo接口&am…...

pga 作用

Oracle pga的作用 PGA 内存结构与功能解释&#xff1a; PGA ├── 1. Private SQL Area ├── 2. Session Memory ├── 3. SQL Work Areas │ ├── Sort Area │ ├── Hash Area │ ├── Bitmap Merge Area │ └── Bitmap Create Area └── 4. Stack S…...

setup.py Pip wheel

. ├── my_package │ ├── __init__.py │ └── my_file.py └── setup.pymy_file.py def my_func():print("Hello World")setup.py from setuptools import setup, find_packages import datetimesetup(namemy_package, # 记得改version0.1.1,packag…...

GO 语言进阶之 时间处理和Json 处理

更多个人笔记见&#xff1a; github个人笔记仓库 gitee 个人笔记仓库 个人学习&#xff0c;学习过程中还会不断补充&#xff5e; &#xff08;后续会更新在github上&#xff09; 文章目录 时间处理基本例子 Json处理基础案例 时间处理 时间格式化必须使用&#xff1a;2006-01-…...

对WireShark 中的UDP抓包数据进行解析

对WireShark 中的UDP抓包数据进行解析 本文尝试对 WireShark 中抓包的 UDP 数据进行解析。 但是在尝试对 TCP 中的 FTP 数据进行解析的时候&#xff0c;发现除了从端口号进行区分之外&#xff0c; 没有什么好的方式来进行处理。 import numpy as np import matplotlib.pyplot …...

Flannel后端为UDP模式下,分析数据包的发送方式(二)

发往 10.244.2.5 的数据包最终会经过物理网卡 enp0s3&#xff0c;尽管路由表直接指定通过 flannel.1 发出。以下以 Markdown 格式详细解释为什么会经过 enp0s3&#xff0c;结合 Kubernetes 和 Flannel UDP 模式的背景。 问题分析 在 Kubernetes 环境中&#xff0c;使用 Flanne…...

从 0 到 1:Spring Boot 与 Spring AI 深度实战(基于深度求索 DeepSeek)

在人工智能技术与企业级开发深度融合的今天&#xff0c;传统软件开发模式与 AI 工程化开发的差异日益显著。作为 Spring 生态体系中专注于 AI 工程化的核心框架&#xff0c;Spring AI通过标准化集成方案大幅降低 AI 应用开发门槛。本文将以国产大模型代表 ** 深度求索&#xff…...

upload-labs通关笔记-第20关 文件上传之杠点绕过

系列目录 upload-labs通关笔记-第1关 文件上传之前端绕过&#xff08;3种渗透方法&#xff09; upload-labs通关笔记-第2关 文件上传之MIME绕过-CSDN博客 upload-labs通关笔记-第3关 文件上传之黑名单绕过-CSDN博客 upload-labs通关笔记-第4关 文件上传之.htacess绕过-CSDN…...

Vscode +Keil Assistant编译报错处理

Vscode Keil Assistant编译报错处理 1.报错图片内容 所在位置 行:1 字符: 25 chcp.com 65001 -Command & c:\Users\92170.vscode\extensions\cl.keil-a … ~ 不允许使用与号(&)。& 运算符是为将来使用而保留的&#xff1b;请用双引号将与号引起来(“&”)&…...

记录python在excel中添加一列新的列

思路是&#xff0c;先将需要添加为新的列存储到一个暂时的列表中&#xff0c;然后用到以下函数来存储 data_.loc[:, "新列的名字"] save_list_ 上面的save_list_就是暂时存储了信息的列表了。 以下是我的代码&#xff0c;供以后快速回忆。 schools_data {"98…...

WebRTC:实时通信的未来之路

WebRTC&#xff1a;实时通信的未来之路 目录 WebRTC&#xff1a;实时通信的未来之路一、背景介绍二、使用方式三、前途展望 一、背景介绍 随着互联网的飞速发展&#xff0c;实时音视频通信需求日益增长。传统的音视频通信多依赖于专有协议和插件&#xff08;如Flash、ActiveX等…...

探索产品经理的MVP:从概念到实践

在产品开发的世界里&#xff0c;MVP&#xff08;Minimum Viable Product&#xff0c;最小可行产品&#xff09;是一个至关重要的概念。它不仅帮助团队快速验证假设&#xff0c;还能降低失败风险&#xff0c;为后续的产品迭代奠定坚实的基础。本文将深入探讨MVP的概念、重要性及…...

用python实现中国象棋

一.象棋规则 象棋是二人对弈的棋类游戏&#xff0c;棋盘由 9 条竖线和 10 条横线交叉构成&#xff0c;中间 “河界” 分楚汉&#xff0c;两端 “九宫” 各 9 个交叉点。棋子分红黑&#xff0c;各 16 枚&#xff0c;含 7 兵种。 1.棋子走法 1.1 红方棋子 帅&#xff1a;1 个…...

GO 语言基础3 struct 结构体

更多个人笔记见&#xff1a; github个人笔记仓库 gitee 个人笔记仓库 个人学习&#xff0c;学习过程中还会不断补充&#xff5e; &#xff08;后续会更新在github上&#xff09; 文章目录 strcut结构体基本例子传入数值和指针的区别初始化方法汇总结构体特点结构体方法定义基于…...

VSCode C/C++ 开发环境完整配置及一些扩展用途(自用)update:2025/3/31

这里主要记录了一些与配置相关的内容。由于网上教程众多&#xff0c;部分解决方法并不能完全契合我遇到的问题&#xff0c;因此我选择以自己偏好的方式&#xff0c;对 VSCode 进行完整的配置&#xff0c;并记录在使用过程中遇到的问题及解决方案。后续内容也会持续更新和完善。…...

iOS 上线前的性能与稳定性检查流程实录:开发者的“最后一公里”(含 KeyMob 应用经验)

一个 iOS 项目写完功能、跑完测试&#xff0c;离上线只差一步了——但很多问题恰恰就在“这最后一公里”暴露&#xff1a;某些设备发热严重&#xff0c;部分流程偶发卡顿&#xff0c;某些崩溃只有长时间运行后才出现。 今天我分享的是我在多个 iOS 项目上线前实际执行过的性能…...

Docker系列(二):开机自启动与基础配置、镜像加速器优化与疑难排查指南

引言 docker 的快速部署与高效运行依赖于两大核心环节&#xff1a;基础环境搭建与镜像生态优化。本期博文从零开始&#xff0c;系统讲解 docker 服务的管理配置与镜像加速实践。第一部分聚焦 docker 服务的安装、权限控制与自启动设置&#xff0c;确保环境稳定可用&#xff1b…...

a16z:AI带来了全新的9种开发软件的模式

非常有启发的9条新兴模式&#xff0c;推荐给已经上手 vibeCoding 的读者们。 开发者正在将 AI 从简单的工具转变为构建软件的新基础。许多核心概念&#xff0c;如版本控制、模板、文档&#xff0c;甚至用户的定义&#xff0c;都在被重新思考。代理&#xff08;Agent&#xff09…...

20.迭代器模式:思考与解读

原文地址:迭代器模式&#xff1a;思考与解读 更多内容请关注&#xff1a;深入思考与解读设计模式 引言 在软件开发中&#xff0c;尤其是在处理集合数据时&#xff0c;你是否曾经遇到过这样的问题&#xff1a;你需要遍历一个集合&#xff08;如数组、列表、集合等&#xff09…...

Java 学习笔记:注解、泛型与 IO 流

目录 课程目标 Java 注解(Annotation) 1. 概念与作用 2. 自定义注解示例 3. JDK 内置注解 4.注释 Java 泛型(Generics) 1. 基本语法 2. 通配符与上下限 3. 常见应用场景 Java IO 流 1. 流的分类1.File文件类 2. 字节流与字符流 3. 经典示例:文件拷贝 总结与…...

在 Excel 使用macro————仙盟创梦IDE

Dim filePath As StringDim fileContent As StringDim lines() As StringDim dataArray() As StringDim lineCount As LongDim maxCols As LongDim i As Long, j As Long 文件路径filePath "" 检查文件是否存在If Dir(filePath) "" ThenMsgBox "文件…...

【MySQL】08.视图

视图就是一个由查询到的内容定义的虚拟表。它和真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表&#xff0c;基表的数据变化也会影响到视图。 1. 基本使用 mysql> select * from user; -------------------- | id | age | name …...

鸿蒙devEco studio如何创建模拟器

官网原文链接&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-emulator-create 操作步骤 点击菜单栏的Tools > Device Manager&#xff0c;点击右下角的Edit设置模拟器实例的存储路径Local Emulator Location&#xff0c;Mac默认存储在~/…...

鸿蒙路由参数传递

页面test.ets 代码如下&#xff1a; import router from ohos.router Entry Component struct Test {State message: string Hello WorldState username: string huState password: string 1build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWe…...