Android多线程机制详细解析

article/2025/9/15 6:51:13
或许你曾经需要项目中进行后台工作比如数据库访问或者网络连接,如果你按照以前的做法,直接在点击事件或者onCreate方法中直接调用访问数据库或者服务器的方法,你就会遇到大多数Android程序员都遇到过的这么一个错误:android.view.ViewRootImpl$CalledFromWrongThreadException


报出这个错误的原因?我们首先要来了解一下Android的多线程机制:

在Android中,必须遵循单线程模式,即:
1.UI线程就是主线程,后台任务不能出现在主线程中,也就是不能阻塞UI线程
2.涉及UI更新的部分不能出现在工作线程中,也就是不要在UI线程之外访问Andoid的UI组件包

Android开发必须满足这两个要素,否则程序就会崩溃,Android开发中既有前台与用户的操作(UI的更新),也有后台与数据的连接(后台任务),比如你有一个页面,用户一点击查询,从后台数据库查询数据,返回给前台页面,页面再将数据罗列展示给用户,在这个过程中涉及到了前后台的交互,如果在点击查询的事件中直接调用select数据库的方法,程序就会崩溃。正确的做法应该是:点击查询后,开启子线程,在子线程里调用访问数据库的方法,然后子线程再将拿到的数据发送给主线程,在主线程中进行展示。



Android的消息机制中,主要涉及到这几个对象:Handler,MessageQueue,Looper,Message

什么是Message?

Message是一种消息体,用于装载需要发送的对象,Android中子线程与主线程之间通信的时候,就需要通过消息来传递,你可以理解为子线程与主线程之间进行“聊天”时所发送的“聊天消息”。


什么是MessageQueue?

MessageQueue消息队列是用来存放所有消息的,遵循先进先出规则(FIFO),如果一个线程需要接收来自其它线程的消息,则必须为其创建一个消息队列,将接收到的消息丢进这个队列中,当需要时再取出来。


什么是Looper?

Looper就相当于管理者的角色,管理当前所属线程的MessageQueue,循环不断地管理MessageQueue接收和分发Message。


什么是Handler?
Handler则相当于处理者的角色,处理和接收Looper派发出来的消息。


我们通过一段简单的代码片段来分析整个流程:


public class HandlerActivity extends Activity {//主线程的handlerprivate Handler mainhandler;//子线程private Thread myThread;//用来展示数据private TextView textView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handler);textView = (TextView)this.findViewById(R.id.hello);mainhandler = new Handler(){public void handleMessage(android.os.Message msg) {switch(msg.what){case 1:textView.setText(msg.what+"已经取到数据");}};};myThread = new Thread(new Runnable(){@Overridepublic void run() {//在此处进行数据库或者网络连接等后台操作Message msg = new Message();msg.obj = "取到的数据:hello";msg.what = 1;mainhandler.sendMessage(msg);}});myThread.start();}}


 

可以看到,首先在主线程中创建了一个mainhandler,用来处理和接收子线程发送过来的消息。再创建了一个子线程myThread,在子线程中进行了访问数据库的操作,访问完成后让message将数据携带上,在这里模拟数据为"取到的数据:hello",通过msg.obj将数据给message,msg.what是标志位,因为本例只有一个子线程,当有多个子线程的时候,handler需要辨识消息是属于哪个子线程发送的,就是通过what变量来分别。mainhandler.sendMessage(msg)是通过mainhandler将消息发送给主线程,在主线程中,通过handleMessage(android.os.Message msg)方法接收到消息,这里的参数正是子线程发送过来的消息,然后再通过switch分支处理消息,并进行UI更新-->textView.setText(msg.what+"已经取到数据")。

你会问,刚才的流程哪里有提到Looper以及MessageQueue?

其实,在刚才的流程中,只涉及了一个子线程,当有多个子线程时,主线程就需要与多个子线程进行交互,这个时候主线程接收到的消息就不止一两条,那么这些消息放在哪里呢?就放在MessageQueue中,而刚才说到handler是用来接收和处理消息的,其实handler并不是直接处理message,而是通过Looper来处理,Looper不断地从MessageQueue中循环取消息,一旦发现MessageQueue中有消息,就将其派发给handler去处理。

整个流程图如下:

AsynTask类:

以上讲解了Android中关于多线程之间的通信原理,但或许你会感觉这样比较麻烦,因此Android提供了AsynTask异步任务类,可以简化了一些工作线程和UI交互的操作:
首先,定义一个类继承于AsynTask类,并重写其中的方法:
class LoginTask extends AsyncTask<String, Void, Integer>{@Overrideprotected Integer doInBackground(String... arg0) {// TODO Auto-generated method stub//可在此处进行各种后台操作if(arg0.equals("")){//如果传进来的参数为空,返回1return 1;}else{//如果传进来的参数不为空,返回2return 2;}}@Overrideprotected void onPostExecute(Integer result) {// TODO Auto-generated method stubsuper.onPostExecute(result);//这里接收的result即为上面doInBackground返回的结果if(result==1){//更新界面UIIntent intent = new Intent(MainActivity.this, HomePageActivity.class);startActivity(intent);}else{//更新界面UInew AlertDialog.Builder(MainActivity.this).setMessage("账号或密码错误!").setPositiveButton("确定", null).show();}}}



其中,AsyncTask泛型有三个参数,第一个表示传入的参数类型,第二个表示进度值,第三个表示传出的结果类型
doInBackground方法是用来填写后台逻辑判断等工作任务,该方法会自动运行并将结果传递给onPostExecute方法
onPostExecute方法接收来自doInBackground的结果,因此,其参数类型要与doInBackground方法的返回参数的类型一致,在这个方法中根据后台传来的结果进行UI的更新。


上面我们已经定义好了一个AsynTask异步任务类,接下来只需要在主线程中调用:

LoginTask loginTask = new LoginTask();
loginTask.execute("1");  //通过execute方法进行运行。


Android线程如何实现循环调用AsynTask:

Handler handler = new Handler(){Runnable runnable = new Runnable(){public void run(){//要做的事情,此处产生AsynTask实例,并executehandler.postDelayed(this,1000);   //每1秒执行一次}};
}



关于Looper.prepare()和Looper.loop()方法:

在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了); 在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息
Android中如果在子线程中新建Handler实例并在其handlerMessage方法中更新UI,则会报错,因为
android中只有主线程是默认带有Looper对象和消息队列的 ,而子线程是没有的,需要自己生成,所以只需要在生成handler实例前调用Looper.prepare()方法,在生成handler实例后调用Looper.loop()方法即可。
其中,Looper.prepare()表示为当前子线程创建一个消息队列,Looper.loop()表示不断地从消息队列中取数据

在一个Thread中Looper也是唯一的,一个Thread对应一个Looper,建立Handler的Looper来自哪个Thread,Handler就属于哪个Thread。如果子线程也需要接收消息时,则需要创建一个消息队列,即在子线程中Looper.prepare()/Looper.loop()

如果在子线程中,新建的handler是:handler = new Handler(Looper.getMainLooper())则表示等下handler发送的消息都仍然是发给主线程,因为现在它所关联的looper依旧是主线程的looper
如果在子线程中,新建的handler是:handler = new Handler(Looper.myLooper())则表示这个handler发送的消息都是发给当前子线程,因为现在它所关联的looper是当前线程的looper

当然,Android还为我们提供了一个HandlerThread类,用来开启一个含Looper对象的线程


该注意的点:

Handler中最好使用obtainMessage来获取消息对象,Handler.obtainMessage()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样能够节省资源消耗。

Android中即使Activity关闭或者onDestroy了,由它创建的子线程依然会继续在后台跑着,不会跟着结束直到系统资源吃紧才会回收,所以要养成习惯,将要关闭的线程在该Activity的onDestroy方法中进行集中关闭:
可以使用handler的handler.removeCallbacks(test)方法关闭子线程。




http://chatgpt.dhexx.cn/article/Dsd2LR7O.shtml

相关文章

Android多线程:请收好这份全面详细的多线程学习指南!

前言 多线程的应用在Android开发中是非常常见的&#xff0c;常用方法主要有&#xff1a; 今天&#xff0c;我将献上一份全面 & 详细的Android多线程学习指南&#xff0c;希望你们喜欢。 Carson带你学多线程系列 基础汇总 Android多线程&#xff1a;基础知识汇总 基础使用…

Android开发之路——多线程

前言 多线程作为Android开发中相对而言较为高阶的知识&#xff0c;其中用到相关的知识点是非常的多&#xff0c;所以在我们需要进行设计或者写多线程的代码就必须要进行相对谨慎的处理&#xff0c;这样就由必要对其要有着比较系统化的认知 Android多线程机制 我们一般将Andro…

Android多线程编程

Android多线程编程 其实就是将耗时操作放在子线程里运行&#xff0c;防止主线程被阻塞&#xff0c;影响软件使用。 线程的基本用法 定义一个线程只需要新建一个类继承自Thread&#xff0c;然后重写父类的run()方法&#xff0c;并在里面编写耗时逻辑即可&#xff0c;如下所示…

Android 多线程应用

传统线程的弊端 1.重复创建线程对象&#xff0c;性能差&#xff1b; 2.线程缺乏统一管理&#xff0c;可能会无限制创建新的线程&#xff0c;线程之间存在CPU资源竞争&#xff0c;导致CPU占用过高&#xff0c;或者发生OOM&#xff1b; 3.缺乏功能&#xff0c;例如定时。线程池的…

android多线程讲解与实例

本期的多线程主题与Android相关&#xff0c;侧重讲解在Android中如何用好多线程&#xff0c;需要你有Java的多线程基础。 首先我们思考几个问题&#xff0c;在Android应用中为什么要用多线程?为了解决哪些问题&#xff1f;或者为了实现哪些功能&#xff1f;有哪些好处&#xf…

Android多线程并发的优化

前言 在日常开发中&#xff0c;多线程无数不在&#xff0c;尤其是android开发&#xff0c;看似业务代码没有调用多线程&#xff0c;实际上也在使用多线程&#xff0c;比如GC线程还有运行在子线程的网络请求。而在使用多线程的时候&#xff0c;不可避免的就需要做好并发安全&am…

Android多线程(Handler篇)

【齐天的博客】转载请注明出处&#xff08;万分感谢&#xff01;&#xff09;&#xff1a; https://blog.csdn.net/qijinglai/article/details/80685226 关联文章: Android多线程(Handler篇) Android多线程(AsyncTask篇) Android多线程(HandlerThread篇) Android多线程(Intent…

Android 多线程实现方式

该原创文章首发于微信公众号“字节流动” Android 多线程实现方式 通常来说&#xff0c;一个应用至少有一个进程&#xff0c;而一个进程至少有一个线程。 线程是 CPU 调度的基本单位&#xff0c;进程是系统资源分配的基本单位。 进程独享内存资源&#xff0c;一个进程可以看…

Java多线程,Android多线程

目录 一、线程的概念 二、线程创建的方式及特点 三、线程创建方式 1、继承Thread类 2、实现Runnable接口 3、实现Callable接口&#xff08;我觉得了解即可&#xff09; 4、AsyncTask异步任务&#xff08;被弃用&#xff09; 5、AsyncTask替代方案 四、线程的基础操作 …

Android多线程开发详解

一、基本概念 1、时间片轮转机制 如果在时间片结束时进程还在运行&#xff0c;则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结来,则CPU当即进行切换。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。 每个进程被分…

Android开发中四种常用的多线程实现方式

前言 一般来说&#xff0c;一个应用至少有一个进程&#xff0c;一个进程至少有一个线程。线程是CPU调度的基本单位&#xff0c;进程是系统资源分配的基本单位。 进程拥有独占的内存资源&#xff0c;一个进程可以看作一个JVM一个进程崩溃后&#xff0c;一般不会影响保护模式下…

Android 中的多线程简介

一、概念讲解 进程&#xff1a;是程序运行过程中系统进行资源分配和调度的一个独立单位&#xff0c;使多个程序可 并发执行&#xff0c;以提高系统的资源利用率和吞吐量。 线程&#xff1a;一个基本的CPU执行单元 & 程序执行流的最小单元。 线程自己不拥有系统资源&#…

anchor free和anchor based的区别

链接&#xff1a;https://www.zhihu.com/question/356551927/answer/926659692 1.目标检测算法一般可分为anchor-based、anchor-free、两者融合类&#xff0c;区别就在于有没有利用anchor提取候选目标框。A. anchor-based类算法代表是fasterRCNN、SSD、YoloV2/V3等fasterRCNN-…

Anchor based and Anchor free(无锚VS有锚)【总结】

anchor-free 和 anchor-based 区别 anchor-free和anchor-based是两种不同的目标检测方法&#xff0c;区别在于是否使用预定义的anchor框来匹配真实的目标框。 anchor-based方法使用不同大小和形状的anchor框来回归和分类目标&#xff0c;例如faster rcnn、retinanet和yolo等。a…

2 anchor-base和anchor_free两者的优缺点

anchor-base和anchor_free两者的优缺点 anchor-base和anchor_free两者的优缺点 一、什么是anchor二、anchor-base和anchor-free的区别三、anchor-free和single anchor三、anchor-base和anchor-free的优缺点 参考 一、什么是anchor 从字面的意思解释&#xff0c;anchor就是船锚…

Anchor-Free总结

目录 Anchor-Free综述 一. CornerNet 1.1 概述1.2 模块介绍 1.2.1 Heatmap1.2.2 Offset1.2.3 Grouping Corners1.2.4 Corner Pooling1.3 总结二. CenterNet 2.1 概述2.2 Center-Regression三. FCOS 3.1. 概述3.2. 模块介绍 3.2.1 论文思路简介3.3.2 回归形式3.3 参考文献四 ATS…

【AI面试】Anchor based 、 Anchor free 和 no anchor 的辨析

深度学习的目标检测算法,通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整(回归)区域边界,从而更准确地预测目标的真实边界框(ground-truth bounding box)。 目标检测算法会需要做两个事情: 推荐区域框是否有目标(positive or …

一文读懂anchor-base和anchor-free

1. 从Faster-RCNN看Anchor Faster-RCNN相对于Fast-RCNN的一个改进是引入了RPN网络&#xff0c;RPN用于区域推荐&#xff0c;替换了此前的SS算法使得网络在整体上更加的CNN化。那么RPN是怎么进行区域推荐的&#xff1f; 简单来说RPN先列举出数万个矩形框&#xff0c;然后用卷积…

目标检测3--AnchorFree的FCOS

文章目录 1.介绍2.FCOS中使用的方法2.1 网络结构2.2FCOS中使用FPN的多级预测2.3FCOS中的中心度 3.mmdetection中FCOS源码参考资料 欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 1.介绍 论文:《FCOS: Fully Convolutional One-Stage Obj…

浅谈Anchor-Free发展历程

1.早期探索&#xff1a; DenseBox: https://arxiv.org/abs/1509.04874 YOLO: https://arxiv.org/abs/1506.02640 2.基于关键点&#xff1a; CornerNet: https://arxiv.org/abs/1808.01244 ExtremeNet: https://arxiv.org/abs/1901.08043 3.密集预测: FSAF: https://arxiv.org/a…