Android 进程与线程:主线程阻塞问题、Handler、WorkThread、线程池工控实践
Android 进程与线程主线程阻塞问题、Handler、WorkThread、线程池工控实践工控设备卡成PPT大概率是你在主线程干了重活这篇把线程那点事儿掰碎了讲清楚。一、Android 进程与线程模型Android 应用启动时系统会为其创建一个进程并在该进程中启动一个主线程Main Thread。主线程也叫UI 线程负责处理界面绘制、用户事件分发、组件生命周期回调等。为什么不能在主线程做耗时操作核心原因一句话主线程承担着每 16ms 左右刷新一次屏幕60fps的重任。如果某个操作阻塞了主线程超过阈值系统就会弹出一个让所有开发者闻风丧胆的东西——ANRApplication Not Responding。常见的耗时操作包括网络请求HTTP / Socket 通信文件 I/O读写本地大文件数据库操作批量增删改查串口数据读取工控场景最常见的坑大量 CPU 计算图像处理、数据解析在工控板子上串口读取如果放在主线程数据量大的时候画面直接冻住按钮点不动这就是典型的主线程阻塞。二、ANR 机制详解ANR 不是随机触发的它有明确的超时阈值ANR 类型触发场景超时阈值Service TimeoutService 的 onCreate / onStartCommand 执行超时前台 20s / 后台 200sBroadcast TimeoutBroadcastReceiver.onReceive 执行超时前台 10s / 后台 60sInput Dispatch Timeout输入事件触摸/按键无响应5sContentProvider Timeout内容提供者发布超时10s注意一个常见误区很多人以为主线程耗时 5 秒就 ANR其实5 秒是输入事件派发超时。如果没有触摸事件进来主线程就算卡 10 秒也不一定弹 ANR——但界面肯定已经卡死了。真正让用户感知到的是输入无响应那条。工控设备往往是长时间无人触屏的所以 ANR 不一定弹出来但界面卡住一样致命——操作员按了停止按钮没反应设备还在跑。三、Handler 消息机制原理Handler 是 Android 线程间通信的核心机制涉及四个关键角色Message消息载体携带what标识、obj数据、arg1/arg2整型参数MessageQueue消息队列按时间排序的单链表结构Looper循环器不断从 MessageQueue 中取出 Message 分发给 HandlerHandler发送和处理消息的入口整个流程可以用一句话概括Handler 发送 Message → MessageQueue 入队 → Looper 循环取出 → Handler.handleMessage() 处理[子线程] Handler.sendMessage(msg) ↓ [MessageQueue] 按 when 排序入队 ↓ [Looper.loop()] 死循环轮询取出 ↓ [Handler.handleMessage()] 在 Handler 创建所在线程执行关键点Handler 的 handleMessage 在哪个线程执行取决于 Handler 在哪个线程创建。在主线程创建的 HandlerhandleMessage 就跑在主线程天然可以更新 UI。四、工作线程创建方式4.1 普通 Thread最原始的方式直接 new 一个线程newThread(()-{// 子线程执行耗时操作StringdatareadSerialPort();// 不能直接更新UI需要Handler切换}).start();缺点每次 new Thread 开销大无法管理线程数量容易造成 OOM。4.2 HandlerThread自带 Looper 的线程适合需要在该线程上创建 Handler 的场景HandlerThreadhandlerThreadnewHandlerThread(serial-worker);handlerThread.start();HandlerserialHandlernewHandler(handlerThread.getLooper()){OverridepublicvoidhandleMessage(NonNullMessagemsg){// 在HandlerThread线程中执行}};4.3 IntentService已废弃了解即可Android 8.0 后官方推荐用JobIntentService / WorkManager替代但在老项目里还能见到。它的特点是串行执行任务执行完自动停止。五、线程池使用线程池是工控开发中管理线程的最佳实践。核心类是ThreadPoolExecutor。5.1 参数详解ThreadPoolExecutorexecutornewThreadPoolExecutor(intcorePoolSize,// 核心线程数即使空闲也保留的线程数intmaximumPoolSize,// 最大线程数队列满后能扩展的最大线程数longkeepAliveTime,// 非核心线程空闲存活时间TimeUnitunit,// 时间单位BlockingQueueRunnableworkQueue,// 任务队列ThreadFactorythreadFactory,// 线程工厂可自定义线程名RejectedExecutionHandlerhandler// 拒绝策略);四种拒绝策略策略行为AbortPolicy默认抛出 RejectedExecutionExceptionCallerRunsPolicy由调用线程执行该任务DiscardPolicy直接丢弃DiscardOldestPolicy丢弃队列最老的任务再尝试提交5.2 工控场景线程池配置工控设备通常资源有限ARM Cortex-A 系列可能只有 1~4 核线程池不能无脑开大。推荐配置// 工控场景线程池核心2线程最大4线程队列128privatestaticfinalintCPU_COUNTRuntime.getRuntime().availableProcessors();privatestaticfinalintCORE_POOL_SIZEMath.max(2,Math.min(CPU_COUNT-1,4));privatestaticfinalintMAX_POOL_SIZECORE_POOL_SIZE*2;privatestaticfinalintQUEUE_CAPACITY128;publicstaticfinalThreadPoolExecutorWORK_EXECUTORnewThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE,30L,TimeUnit.SECONDS,newLinkedBlockingQueue(QUEUE_CAPACITY),newThreadFactory(){privatefinalAtomicIntegermCountnewAtomicInteger(1);OverridepublicThreadnewThread(Runnabler){returnnewThread(r,control-worker-mCount.getAndIncrement());}},newThreadPoolExecutor.CallerRunsPolicy()// 队列满了让调用线程自己跑不丢任务);工控场景拒绝策略推荐CallerRunsPolicy——宁可让提交任务的线程自己执行也不能丢任务。丢一个继电器控制指令可能就是事故。六、工控实践串口数据读取用子线程 Handler 更新 UI下面是一个完整的串口通信线程 Handler 更新 UI 的实现publicclassSerialPortManager{privatestaticfinalintMSG_DATA_RECEIVED0x01;privatestaticfinalintMSG_ERROR0x02;// 主线程Handler用于更新UIprivatefinalHandlermUiHandler;// 串口工作线程privateHandlerThreadmSerialThread;privateHandlermSerialHandler;// 串口运行标志privatevolatilebooleanmRunningfalse;publicSerialPortManager(HandleruiHandler){this.mUiHandleruiHandler;}/** * 初始化串口读取线程 */publicvoidstart(){mSerialThreadnewHandlerThread(serial-read-thread);mSerialThread.start();mSerialHandlernewHandler(mSerialThread.getLooper()){OverridepublicvoidhandleMessage(NonNullMessagemsg){if(msg.whatMSG_DATA_RECEIVED){byte[]data(byte[])msg.obj;parseAndNotify(data);}}};mRunningtrue;// 启动读取循环mSerialHandler.post(this::readLoop);}/** * 串口读取循环在子线程执行 */privatevoidreadLoop(){while(mRunning){try{byte[]buffernewbyte[1024];intsizemInputStream.read(buffer);// 阻塞读取if(size0){byte[]datanewbyte[size];System.arraycopy(buffer,0,data,0,size);// 通过Handler切到主线程MessagemsgmUiHandler.obtainMessage(MSG_DATA_RECEIVED,data);mUiHandler.sendMessage(msg);}}catch(IOExceptione){mUiHandler.obtainMessage(MSG_ERROR,e.getMessage()).sendToTarget();break;}}}/** * 解析数据并通知UI */privatevoidparseAndNotify(byte[]data){// 协议帧解析帧头0xAA 长度 数据 校验if(data.length4)return;if(data[0]!(byte)0xAA)return;intlendata[1]0xFF;// ...解析逻辑}publicvoidstop(){mRunningfalse;if(mSerialThread!null){mSerialThread.quitSafely();}}}在 Activity 中使用publicclassMainActivityextendsAppCompatActivity{privateTextViewtvTempData;privateSerialPortManagermSerialManager;// UI Handler处理子线程发来的消息privatefinalHandlermUiHandlernewHandler(Looper.getMainLooper()){OverridepublicvoidhandleMessage(NonNullMessagemsg){switch(msg.what){caseSerialPortManager.MSG_DATA_RECEIVED:byte[]data(byte[])msg.obj;StringhexStrbytesToHex(data);tvTempData.setText(收到数据: hexStr);break;caseSerialPortManager.MSG_ERROR:Toast.makeText(MainActivity.this,串口错误: msg.obj,Toast.LENGTH_SHORT).show();break;}}};OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tvTempDatafindViewById(R.id.tv_temp_data);mSerialManagernewSerialPortManager(mUiHandler);mSerialManager.start();}OverrideprotectedvoidonDestroy(){super.onDestroy();mSerialManager.stop();}}6.1 核心要点总结要点说明串口读取必须在子线程InputStream.read()是阻塞调用放主线程必卡UI 更新必须切回主线程子线程直接操作 View 会抛异常Handler 是线程切换的标准方式轻量、可控、生命周期清晰HandlerThread 适合持续轮询场景自带 Looper省去手动 prepare/loopvolatile 控制线程退出保证可见性避免死循环工控开发中线程管理是基本功。串口、网络、定时采集每一路数据流都应该有独立的工作线程通过 Handler 或回调将结果送到主线程刷新界面。把线程用好设备才能跑得稳。

相关新闻

Dify实战指南:2小时构建AI Agent与智能工作流

Dify实战指南:2小时构建AI Agent与智能工作流

最近在尝试将大语言模型(LLM)能力集成到业务中时,你是否也遇到过这些困扰:想快速构建一个智能对话应用,却卡在复杂的模型API调用、上下文管理、工具集成和前端开发上?面对琳琅满目的Agent框架,感…

2026/9/18 16:33:19 阅读更多 →
Android 存储体系:内部存储、SD 卡、U 盘挂载读写、工控文件持久化方案

Android 存储体系:内部存储、SD 卡、U 盘挂载读写、工控文件持久化方案

Android 存储体系:内部存储、SD 卡、U 盘挂载读写、工控文件持久化方案 工控设备的数据比女朋友还难伺候——丢了不行、路径搞错也不行、U盘没挂载就写更不行。今天就把Android存储体系给你从头捋到脚。 一、Android 存储架构总览 Android的存储体系可以简化成三大…

2026/9/22 0:11:22 阅读更多 →
HarmonyOs应用《日记本》开发第10篇 - @Builder 构建器与组件复用

HarmonyOs应用《日记本》开发第10篇 - @Builder 构建器与组件复用

在 ArkUI 开发中,Builder 是实现 UI 片段复用的核心装饰器。它让开发者可以将复杂的 UI 拆分为多个可维护的方法,提高代码的可读性和复用性。本篇将以日记项目的三个页面为例,深入探讨 Builder 的使用模式和最佳实践。Builder 基本语法 Build…

2026/9/20 20:21:10 阅读更多 →

最新新闻

3个坑教你用Python写定制家具拆单软件最佳实践

3个坑教你用Python写定制家具拆单软件最佳实践

3个坑教你用Python写定制家具拆单软件最佳实践 刚学完 Python 语法,盯着屏幕发呆,心里只有一句话: 学会语法却不知怎么搭项目 。 你背下了 for 循环,记住了 class…

2026/9/23 3:46:24 阅读更多 →
私域流量的终局是什么?从收割到经营的转变

私域流量的终局是什么?从收割到经营的转变

2020年:私域是风口,赶紧加人。 2022年:私域红利没了,群全死了。 2024年:私域还能做吗?怎么做? 私域没死,死的是收割思维。 今天聊聊:私域的终局是什么,以及怎…

2026/9/23 3:46:24 阅读更多 →
VulnHub靶机Raven 1实战:从PHPMailer RCE到MySQL UDF提权

VulnHub靶机Raven 1实战:从PHPMailer RCE到MySQL UDF提权

如果你跟着VulnHub系列一路打到这一篇,应该会有个感受:纯堆漏洞的靶场玩多了容易飘,真正练手的是那种能把整个入侵链路串起来的靶场。Raven 1就是后者。它是VulnHub上知名度很高的一台入门级靶机,虚构了一家叫Raven Security的公司…

2026/9/23 3:46:24 阅读更多 →
Diem Framework 形式化验证指南:Move 规范语言与 Move Prover 的工程化实践

Diem Framework 形式化验证指南:Move 规范语言与 Move Prover 的工程化实践

Diem Framework 形式化验证指南:Move 规范语言与 Move Prover 的工程化实践 【免费下载链接】diem Diem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world. 项目地址: https://gitcode.…

2026/9/23 3:46:24 阅读更多 →
网线选型与制作全指南:从线序到故障排查

网线选型与制作全指南:从线序到故障排查

1. 网线类别与选型:别只认Cat5e1.1 从Cat5e到Cat8:类别差异与适用场景网线这东西,平时没人关注,一旦网络出问题,第一个被怀疑的就是它。我在IT运维这块摸爬滚打了十几年,可以很负责任地说:很多“…

2026/9/23 3:46:24 阅读更多 →
OSS-Fuzz 与 ClusterFuzz:分布式模糊测试基础设施的完整使用指南

OSS-Fuzz 与 ClusterFuzz:分布式模糊测试基础设施的完整使用指南

OSS-Fuzz 与 ClusterFuzz:分布式模糊测试基础设施的完整使用指南 【免费下载链接】oss-fuzz OSS-Fuzz - continuous fuzzing for open source software. 项目地址: https://gitcode.com/gh_mirrors/os/oss-fuzz 导读 本文聚焦于 OSS-Fuzz 项目背后的分布式模…

2026/9/23 3:45:24 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →