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

android多线程与线程间通信

一、核心概念:为什么需要多线程?

1. 主线程(UI 线程)

  • 负责处理用户交互(点击、触摸)、绘制UI、更新界面。
  • 黄金法则:绝对不能在主线程执行耗时操作!
  • 如果主线程被阻塞超过 5秒,系统会抛出 Application Not Responding (ANR) 错误,导致应用崩溃。

2. 工作线程(后台线程)
用于执行所有可能阻塞主线程的任务,例如:

  • 网络请求(Retrofit, Volley)
  • 数据库操作(Room)
  • 读写文件
  • 解析大型数据(JSON, XML)
  • 复杂计算

二、Android线程间通信

Android 线程间通信是一个核心话题。由于 Android 的 UI 操作必须在主线程(UI线程)执行,而耗时工作必须在后台线程执行,因此线程间通信是必不可少的。

1.Handler & Looper & MessageQueue (最基础的底层机制)

这是 Android 线程间通信的基石,几乎所有其他高级方案都基于此。

  • Looper (循环器): 一个不断从 MessageQueue 中取出 Message 或 Runnable 的循环。线程默认没有 Looper,需要调用 Looper.prepare() 和 Looper.loop() 来创建和启动。主线程(UI线程)自带一个 Looper。
  • MessageQueue (消息队列): 一个单向链表结构的消息队列,用于存放 Handler 发送来的 Message。
  • Handler (处理器): 用于发送和处理 Message 或 Runnable 对象。每个 Handler 都会绑定到一个特定的线程(即该线程的 Looper)上。
    工作流程:
  • 工作线程使用 Handler 将 Message 或 Runnable 发送到主线程的 MessageQueue 中。
  • 主线程的 Looper 不断循环,从 MessageQueue 中取出这些消息。
  • 取出的消息最终会由主线程的 Handler 的 handleMessage() 方法处理,或者直接执行 Runnable 的 run() 方法。此时,代码就在主线程中执行了。

代码示例:

// 在主线程中创建Handler,并关联主线程的Looper
Handler mainHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);// 在这里处理消息,此方法运行在主线程if (msg.what == 1) { // 根据消息标识判断String data = (String) msg.obj; // 获取消息内容textView.setText(data); // 安全更新UI}}
};// 在工作线程中发送消息
new Thread(() -> {// ... 执行耗时操作,比如网络请求String result = "Data from network";// 方式1:发送MessageMessage message = mainHandler.obtainMessage();message.what = 1; // 自定义的消息标识message.obj = result; // 要传递的数据mainHandler.sendMessage(message);// 方式2:更简单的,发送一个RunnablemainHandler.post(() -> {// 这个Runnable里的代码会在主线程执行textView.setText(result);});
}).start();

2.AsyncTask (已废弃,但需了解)

AsyncTask 内部封装了 Handler 和线程池,提供了 onPreExecute, doInBackground, onProgressUpdate, onPostExecute 等回调方法,使得“后台工作-更新进度-UI更新”的流程非常清晰。
由于其内存泄漏、生命周期管理等问题,官方已正式废弃它。但它的设计思想值得了解。

3.HandlerThread

一个自带 Looper 的 Thread 子类。它为你准备好了 Looper,你只需要为其创建 Handler 即可与之通信。
适用场景:需要一个长时间运行、且有自己消息循环的后台线程。

// 1. 创建并启动HandlerThread
HandlerThread handlerThread = new HandlerThread("MyBackgroundThread");
handlerThread.start();// 2. 获取它的Looper,并创建与之绑定的Handler
Handler backgroundHandler = new Handler(handlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {// 这个handleMessage运行在handlerThread这个后台线程中!// 可以在这里执行耗时任务doHeavyWork();}
};// 3. 在主线程向这个后台线程发送任务
backgroundHandler.post(() -> {// 这个Runnable会在HandlerThread中执行
});// 4. 退出时记得释放资源
handlerThread.quitSafely();

4.LiveData (生命周期感知的数据持有者)

LiveData 是一种可观察的数据存储器类,具有生命周期感知能力。它通常和 ViewModel 一起使用。

工作原理:当 LiveData 所持有的数据发生变化时,它会通知处于活跃生命周期状态(如 STARTED, RESUMED)的观察者。
线程通信:LiveData 的 postValue() 方法可以在后台线程调用,它会自动将值派发到主线程,然后通知观察者。setValue() 必须在主线程调用。

// 在ViewModel中
public class MyViewModel extends ViewModel {private MutableLiveData<String> mData = new MutableLiveData<>();public LiveData<String> getData() {return mData;}public void fetchData() {new Thread(() -> {// 在后台线程工作String newData = expensiveOperation();// 使用postValue从后台线程更新LiveDatamData.postValue(newData); // 自动切到主线程通知Observer}).start();}
}// 在Activity/Fragment中观察
myViewModel.getData().observe(this, newData -> {// 这个Observer回调在主线程执行,可以安全更新UItextView.setText(newData);
});

5. Kotlin Coroutines (协程) + 调度器 (Dispatchers)

这是当前 最主流、最简洁 的异步处理和线程通信方案。它用看似同步的代码写出了异步操作。

核心是通过挂起函数(suspend) 和协程上下文(CoroutineContext) 中的调度器(Dispatchers) 来切换线程。

// 在ViewModel中
fun fetchData() {viewModelScope.launch { // 在主线程启动协程// 1. 在主线程更新UI:显示加载框loadingIndicator.value = true// 2. 切换到IO线程执行耗时操作(不会阻塞主线程)val result = withContext(Dispatchers.IO) {// 这个代码块在IO线程执行networkRequest() // 模拟网络请求}// 3. withContext结束后,自动切回原来的线程(主线程)// 4. 在主线程更新UItextView.text = resultloadingIndicator.value = false}
}

Dispatchers 的类型:

  • Dispatchers.Main: 用于更新UI。
  • Dispatchers.IO: 用于网络、磁盘操作。
  • Dispatchers.Default: 用于CPU密集型计算。

6.RxJava (响应式扩展)

一个基于事件流的异步编程库,功能极其强大,但学习曲线较陡。它通过 observeOn 和 subscribeOn 操作符来精确控制线程切换。

Observable.fromCallable(() -> {// 在IO线程执行耗时任务 (因为subscribeOn)return doHeavyWorkInBackground();
})
.subscribeOn(Schedulers.io()) // 指定上游执行的线程
.observeOn(AndroidSchedulers.mainThread()) // 指定下游观察者的线程
.subscribe(result -> {// 在主线程接收结果,更新UItextView.setText(result);
});

三、基础java方案

7.Thread 和 Runnable

最基础的创建线程的方式。

// 方式一:实现 Runnable 接口
Thread thread = new Thread(new Runnable() {@Overridepublic void run() {// 在后台线程执行耗时任务doHeavyWork();// 不能在这里直接更新UI!// runOnUiThread(() -> textView.setText("Done")); // 需要特殊方法}
});
thread.start();// 方式二:使用Lambda表达式(Java 8+)
new Thread(() -> {// 后台工作
}).start();

缺点:难以管理、复用;需要自己处理线程间通信。

8.ExecutorService(线程池)

管理和复用线程的最佳Java实践。避免了频繁创建和销毁线程的开销。

// 1. 创建固定大小的线程池(推荐)
ExecutorService executor = Executors.newFixedThreadPool(4);// 2. 提交任务(Runnable 或 Callable)
executor.execute(new Runnable() {@Overridepublic void run() {doHeavyWork();}
});// 3. 如果需要获取后台任务的结果,使用 Callable 和 Future
Future<String> future = executor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {doHeavyWork();return "Result";}
});// 在需要结果的时候获取(会阻塞当前线程)
try {String result = future.get();runOnUiThread(() -> updateUI(result));
} catch (Exception e) {e.printStackTrace();
}// 4. 关闭线程池(通常在应用退出时)
executor.shutdown();

四、总结与对比

机制特点适用场景现代性
Handler/Looper底层基础,灵活但稍显繁琐需要精细控制消息、自定义后台循环传统,但必不可少
HandlerThread自带Looper的Thread需要一个常驻的、有消息循环的后台线程传统
LiveData生命周期感知,与ViewModel搭配基于生命周期的UI数据更新现代 (推荐)
Kotlin Coroutines代码简洁,结构化并发,易于管理几乎所有异步场景的首选最现代 (强烈推荐)
RxJava功能强大,流操作符丰富复杂的异步事件流处理现代,但复杂度高

最佳实践建议:
对于新项目:毫不犹豫地选择 Kotlin 协程 + LiveData/StateFlow。这是 Google 官方主推的现代方案。
理解底层:理解 Handler/Looper 机制有助于你更好地理解其他高级框架的原理。
避免使用:AsyncTask 和直接使用裸 Thread 进行复杂通信,因为它们难以维护且容易出错。

http://www.dtcms.com/a/351606.html

相关文章:

  • python-多线程(笔记)(持续更新)
  • 基于dify+ollama+bge组合搭建本地知识库
  • 10分钟快速搭建 SkyWalking 服务
  • 【Apache MXNet】
  • Med-SA 论文总结
  • Apache Shiro基本使用指南
  • 基于SpringBoot的社团管理系统【2026最新】
  • 《C++ Primer 第五版》initializer_list 涉及到的范围 for 循环(range-based for) 的语义差别
  • 车载铁框矫平机:把“钣金诊所”开到工地上
  • 【软考论文】论原型开发方法及其应用
  • Ubuntu 24.04 LTS 桌面版安装问题记录
  • 2025年8月27日,七月初五,生活指南
  • Python包管理与安装机制详解
  • pytorch-利用letnet5框架深度学习手写数字识别
  • 漫谈《数字图像处理》之霍夫变换发展历程与演进脉络
  • 类似ant design和element ui的八大Vue的UI框架详解优雅草卓伊凡
  • (vue)el-progress左侧添加标签/名称
  • C++学习(4)模板与STL
  • 虚幻5引擎:我们是在创造世界,还是重新发现世界?
  • 8.26 review
  • 【大前端】React统计所有网络请求的成功率、失败率以及统一入口处理失败页面
  • Ubuntu22.04安装OBS
  • 嵌入式系统学习Day23(进程)
  • 2025.8.26总结
  • 【系统架构设计(二)】系统工程与信息系统基础中:信息系统基础
  • 数据结构青铜到王者第四话---LinkedList与链表(1)
  • 【SystemUI】新增实体键盘快捷键说明
  • 【SystemUI】锁屏点击通知显示的解锁界面和通知重叠
  • [Sync_ai_vid] 唇形同步推理流程 | Whisper架构
  • 技术分享︱国产化突破:开源MDO工具链在新一代神威超算上的安装与调试