发布时间:2026/9/2 6:59:13
Android条码扫描Demo:集成CameraX与ML Kit实现 简介面向Android开发者的条码扫描集成示例基于ZXing开源库附带完整可运行的扫码项目。Demo覆盖从相机预览、条码检测到解码返回的完整链路支持QR码、Code 128、EAN-13等多种格式适合需要在App中快速落地扫码功能的初中级开发者参考。压缩包共262个文件以77个Java源码、61个XML布局与配置、91个HTML说明页面为主另含jar依赖与界面图片整体大小2.9MB目录结构清楚可直接导入工程研读。通过学习可重点掌握ZXing库的依赖配置、CaptureActivity的启动与自定义、相机运行时权限申请、扫描结果在onActivityResult中的解析以及按业务需求限制解码格式、调整扫描区域等优化手段并了解相机无法访问、解码失败时的处理思路。资源还涉及多格式支持、离线解码与性能调优配置便于读者掌握条码扫描的完整实现思路。目前已有873人学习下载对理解Android相机API、Intent交互和硬件权限管理均有直接帮助。1. 写这个扫码Demo之前先想清楚方案做 Android 条码扫描 demo 的场景我猜大多数人跟我一样不是要做一套完整的扫码支付级 SDK而是要在自己的 App 里先跑通一个“能扫、能出结果、能演示”的最小闭环。我这次接的需求也简单——内部工具里加一个扫码入口用来扫商品条码和二维码识别后把结果回填到表单。网上搜 android 条码扫描 demo很多教程还停留在 ZXing 时代要么给你一个老项目让你 clone要么源码里塞了一堆用不上的逻辑。我干脆用 CameraX 加 ML Kit 从零搭了一个干净版本这里把完整思路和踩过的坑一起写出来。1.1 需求其实很明确最小可用闭环这个 demo 不是要做一个扫码 App它的目标是验证三件事能不能打开相机预览、能不能对准条码自动识别、识别结果能不能回到业务层。只要这三点成立后续接业务逻辑就是半小时的事。所以我给自己定的验收标准很简单——打开页面自动出画面对准一张 EAN-13 商品码或者二维码1 秒内出结果TextView 显示原始内容页面不闪退、不卡死。1.2 为什么用 CameraX ML Kit而不是 ZXing当时我在三个方案里反复犹豫方案优点缺点ZXing Core CameraX体积小、文档多需要自行封装相机与解码流程CameraX ML Kit bundled离线可用、识别率高、接口干净APK 增加 23MBCameraX ML Kit unbundled包体积几乎不增依赖系统级服务部分设备无法使用最后我选了 bundled 版 ML Kit图一个省心。ML Kit 的 barcode-scanning 模型直接在本地跑适配 EAN-13、Code 128、QR Code 等常用格式不需要自己做图像预处理也不依赖网络。CameraX 则负责相机预览和帧回调它把 Camera2 那套复杂的状态机封装成了几个用例对象配合生命周期绑定页面切走自动释放相机省掉大量样板代码。1.3 Demo 的运行效果和适用范围跑通后的效果基本符合预期相机预览流畅条码对准画面中央后大约 500ms 以内出结果横向条码和竖屏二维码都能识别。适用范围很清晰——任何需要在 App 内快速集成扫码能力的 Android 项目无论是库存盘点、票据录入还是扫码登录都可以拿这个 demo 当起点。要接生产环境请继续看到最后一节。2. 环境准备与依赖配置2.1 Android Studio 与 SDK 版本选择我用的版本组合是 Android Studio Hedgehog2023.1.1Gradle 8.2AGP 8.2.2compileSdk 34minSdk 26。如果你用的是更新的 IDE 版本AGP 版本别乱升保持默认即可。这里有一个容易踩的坑CameraX 1.3.x 和 ML Kit 17.x 对 compileSdk 版本有要求低于 33 会直接编译报错。建议直接建一个新工程包名随意语言选 Kotlin空 Activity 模板就够了。2.2 引入 CameraX 和 ML Kit 依赖在 app 模块的 build.gradle 里加上这段依赖dependencies { implementation androidx.core:core-ktx:1.12.0 implementation androidx.appcompat:appcompat:1.6.1 implementation com.google.android.material:material:1.11.0 implementation androidx.constraintlayout:constraintlayout:2.1.4 def camerax_version 1.3.1 implementation androidx.camera:camera-core:${camerax_version} implementation androidx.camera:camera-camera2:${camerax_version} implementation androidx.camera:camera-lifecycle:${camerax_version} implementation androidx.camera:camera-view:${camerax_version} implementation com.google.mlkit:barcode-scanning:17.3.0 }这里有几个细节需要说明。CameraX 的五个子模块各有分工camera-core 是核心接口camera-camera2 是底层实现camera-lifecycle 负责绑定生命周期camera-view 提供 PreviewView 控件。ML Kit 的 barcode-scanning 是 bundled 版本首次启动后不再依赖外部服务离线也能扫。2.3 权限声明与布局搭建AndroidManifest.xml 里加权限uses-permission android:nameandroid.permission.CAMERA / uses-feature android:nameandroid.hardware.camera android:requiredfalse /uses-feature 声明为 false是为了让没有摄像头的设备也能安装 APK虽然实际没什么用但算个好习惯。布局文件用最简单的方式全屏 PreviewView 作为相机预览层顶部一个 TextView 显示结果底部一个按钮控制闪光灯方便测试暗光环境androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto android:layout_widthmatch_parent android:layout_heightmatch_parent androidx.camera.view.PreviewView android:idid/previewView android:layout_width0dp android:layout_height0dp app:layout_constraintTop_toTopOfparent app:layout_constraintBottom_toBottomOfparent app:layout_constraintStart_toStartOfparent app:layout_constraintEnd_toEndOfparent / TextView android:idid/resultText android:layout_widthmatch_parent android:layout_heightwrap_content android:background#80000000 android:padding12dp android:textColor#FFFFFF android:textSize16sp app:layout_constraintTop_toTopOfparent / Button android:idid/flashButton android:layout_widthwrap_content android:layout_heightwrap_content android:text手电筒 app:layout_constraintBottom_toBottomOfparent app:layout_constraintEnd_toEndOfparent app:layout_constraintStart_toStartOfparent / /androidx.constraintlayout.widget.ConstraintLayout布局里 TextView 背景用了半透明黑是为了在任何场景下都有可读性。实际项目中你可以换成扫码框动画、取景框之类的交互元素但 demo 阶段不需要花哨。3. 核心代码把扫码流程串起来3.1 预览与图像分析CameraX 的职责划分CameraX 的核心思想是把相机能力拆成三个用例Preview预览、ImageAnalysis帧分析、ImageCapture拍照。扫码场景只需要 Preview 和 ImageAnalysis 同时运行两者通过 bindToLifecycle 绑定到同一个生命周期持有者。这里要理解两个关键点第一Preview 只负责把相机画面渲染到 PreviewView 上它不参与数据分析。第二ImageAnalysis 会不断从相机流中取出图像帧回调给 Analyzer由你的代码决定如何处理。扫码识别的核心就在 Analyzer 里所以 Preview 和 ImageAnalysis 需要同时 bind缺一个都不行代码里如果只 bind 了 ImageAnalysis界面上是黑的别问我怎么知道的。3.2 条码解析ML Kit 的调用姿势ML Kit 的条码识别 API 调用非常简单核心就三个类BarcodeScannerOptions 用来指定识别格式BarcodeScanning 创建扫描器实例InputImage 把 CameraX 的 ImageProxy 包装成 ML Kit 能识别的图像对象。在 Activity 里绑定相机private fun startCamera() { val cameraProviderFuture ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ val cameraProvider cameraProviderFuture.get() cameraProvider.unbindAll() val preview Preview.Builder().build().also { it.setSurfaceProvider(binding.previewView.surfaceProvider) } val analysis ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() analysis.setAnalyzer(Executors.newSingleThreadExecutor()) { imageProxy - handleImageProxy(imageProxy) } val selector CameraSelector.DEFAULT_BACK_CAMERA cameraProvider.bindToLifecycle(this, selector, preview, analysis) }, ContextCompat.getMainExecutor(this)) }这段代码有几个值得解释的选项。setTargetResolution(Size(1280, 720)) 指定分析帧的分辨率720P 在识别精度和性能之间比较平衡分辨率太大徒增耗电太小条码容易糊。setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST) 表示如果上一帧还没处理完直接丢掉最新帧保证 Analyzer 永远处理的是最新画面不会积压导致卡顿。3.3 结果回传与界面更新handleImageProxy 就是整个扫码 demo 的核心逻辑private val scanner BarcodeScanning.getClient( BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_QR_CODE, Barcode.FORMAT_EAN_13, Barcode.FORMAT_EAN_8, Barcode.FORMAT_CODE_128 ) .build() ) private var canScan true private fun handleImageProxy(imageProxy: ImageProxy) { val mediaImage imageProxy.image if (mediaImage null) { imageProxy.close() return } val image InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) scanner.process(image) .addOnSuccessListener { barcodes - if (barcodes.isNotEmpty() canScan) { canScan false barcodes.firstOrNull()?.rawValue?.let { value - runOnUiThread { binding.resultText.text value // 这里就可以把 value 回传给你的业务层 } } } } .addOnCompleteListener { imageProxy.close() } }这里有两个细节必须注意。第一imageProxy.close() 一定要在回调里调用否则相机帧资源不释放连续扫码几分钟后画面会卡死。我一开始把 close() 写在 process 之后立刻调用结果 ML Kit 还在读帧就释放了崩溃概率极高。正确姿势是放在 addOnCompleteListener 里无论识别成功还是失败都关闭。第二canScan 标志用来控制扫码频率否则同一张条码在画面里停留两秒回调可能会触发十几次业务层会被轰炸。3.4 闪光灯与权限处理MainActivity 里的权限逻辑也比较常规。Android 6.0 以上需要运行时权限申请的结果回调里启动相机。闪光灯用 CameraControl 来切换binding.flashButton.setOnClickListener { val cameraControl cameraProviderFuture.get().bindToLifecycle( this, CameraSelector.DEFAULT_BACK_CAMERA, preview, analysis ).cameraControl cameraControl.enableTorch(torchOn) }我这里简化了逻辑实际项目建议把 cameraProvider 缓存成字段按钮点击时直接复用。一个小技巧如果相机被其他应用占用bindToLifecycle 会抛异常可以在调用前后用 try-catch 包住弹出 Toast 提示用户。4. 常见问题与排查实录4.1 画面模糊、对焦拉风箱模拟器上跑这个 demo画面糊是正常的真机上如果糊大概率是 CameraX 的对焦模式和扫描距离问题。CameraX 默认会自动对焦但条码识别对清晰度要求高尤其是细密的一维条码。我的处理是保持手机离条码 10~20cm让条码占据画面宽度的三分之一以上。如果暗光环境自动对焦跟不上就打开闪光灯实测在室内灯光不足时能把识别率从六成拉到九成以上。4.2 扫出来的中文是乱码二维码很多时候存的是中文文本ML Kit 返回的 rawValue 如果出现乱码大概率是编码格式问题。二维码内容可能是 GBK 编码而 SDK 默认按 UTF-8 解码。遇到这种情况可以尝试手动转码再展示private fun decodeRawValue(rawValue: String?): String? { if (rawValue null) return null return try { String(rawValue.toByteArray(Charsets.ISO_8859_1), Charsets.UTF_8) } catch (e: Exception) { rawValue } }注意这个方法不一定对每个乱码都有效因为不同二维码生成工具的编码策略不同。踩了坑之后我的建议是业务上约定好统一用 UTF-8 生成二维码从源头规避比在识别端做各种猜测省事得多。4.3 相机被占用或预览黑屏预览黑屏最常见原因是 Activity 启动时立刻绑定相机但权限还没申请成功或者 PreviewView 没设置 SurfaceProvider。另一个场景是已绑定相机后再次调用 bindToLifecycle不先 unbindAll() 就会报错画面停在黑屏状态。我建议在 startCamera 开头固定加一行 cameraProvider.unbindAll()无论首次还是后续调用都安全。还有一种情况是相机被系统相机应用或其他扫码 App 占用模拟器上尤其常见重启模拟器通常能解决。4.4 一次扫描频繁回调这是扫码场景最典型的业务问题。条码一直在画面里Analyzer 每帧都识别成功回调被无限触发。我前面用 canScan 做了简单拦截真实业务里还可以加个延时恢复机制——比如识别成功后 2 秒内忽略新结果2 秒后 allowScan 置回 trueprivate fun resetScan() { Handler(Looper.getMainLooper()).postDelayed({ canScan true }, 2000) }这个 2 秒延时不仅防抖还给了用户移动手机扫描下一条码的时间不会因为同一张条码反复触发而崩溃。4.5 ML Kit 初始化失败unbundled 版本如果在没有对应系统服务的设备上运行初始化时会直接崩日志里能看到“com.google.android.gms.common.internal”相关异常。我选择 bundled 依赖后这个坑就消失了代价是 APK 体积增加约 2.5MB在现在的应用体量下可以接受。如果你的项目对包体积极其敏感可以考虑用 ZXing Core 替代但识别精度和开发效率会有取舍。4.6 坐标与旋转偏离有同事问过我扫码框明明对准了条码结果区域却偏离。这涉及 ImageProxy 图像的旋转问题。CameraX 的 previewView 会正确适配屏幕旋转但 ImageAnalysis 的帧默认是传感器方向的必须通过 imageProxy.imageInfo.rotationDegrees 传给 ML KitML Kit 内部才会正确处理坐标系。如果你的代码里固定传 0竖屏扫码时 boundingBox 坐标就会错位。这属于比较容易忽视的细节代码里我已经做了正确处理。5. 从 Demo 到正式功能的几点经验5.1 把 Analyzer 做成独立模块demo 跑通后第一件事就是把扫码逻辑从 Activity 里抽出来。我建议定义一个 BarcodeAnalyzer 类构造参数传入识别结果回调class BarcodeAnalyzer( private val onBarcodeDetected: (String) - Unit ) : ImageAnalysis.Analyzer { private val scanner ... override fun analyze(imageProxy: ImageProxy) { ... } }这样 Activity 只负责生命周期和 UI扫码逻辑可以复用到任何一个页面甚至能做成独立的 library module。我做内部工具时销售端、仓库端、后台录入页面都要扫码每次都是复用同一个 Analyzer只换了回调里的业务处理改造成本极低。5.2 连续扫码模式与节流如果要盘点一整箱货每次扫码需要连续扫多个条码。这时 canScan 标志会挡住后续操作我改成 RestartableAnalyzer——每次识别成功后弹出一个确认对话框用户点击“确定”后才恢复扫码。有些场景需要自动连续扫码可以在列表底部显示新扫到的条码并添加去重逻辑避免同一箱货扫了两次。去重不能只比对 rawValue因为不同商品可能有相同的条码前缀建议比对“条码值 时间戳 用户 ID”的组合。5.3 相册选图识别有些用户拍照传图扫码的需求也很常见。做法是拿到相册图片的 Uri 后用 BitmapFactory 解码成 Bitmap再通过 InputImage.fromBitmap(bitmap, 0) 送入 ML Kit 识别。注意相册图片分辨率很高直接解码容易 OOM可以先做降采样val bounds BitmapFactory.decodeResource(resources, R.drawable.placeholder).bounds // 用 BitmapFactory.Options.inSampleSize 按需采样这个功能作为兜底很有用尤其是一些印刷质量差、相机对焦困难的条码用户直接拍一张清晰照片识别成功率很高。5.4 性能与包体积考虑我用真实场景对比过CameraX 默认的 720P 分析帧在骁龙 7 系处理器上CPU 占用约 15%识别耗时平均 200ms帧率稳定在 20FPS 以上日常使用没压力。如果目标设备性能较差可以把 targetResolution 降到 640x480识别速度更快但远距离小条码会吃力。ML Kit bundled 依赖对 start-up 时间的影响约 50ms基本无感知。如果 App 已经引入了 Firebase ML Kit 的其他模块可以直接复用同一套依赖避免重复初始化。这个 demo 我前后跑了一个下午最深的感受是CameraX 和 ML Kit 的组合已经把扫码的复杂度压到了最低剩下的功夫全在细节。比如 imageProxy.close() 的时机、旋转角度的传递、扫码节流的设计这些都是文档里不容易提示、但直接影响项目能不能上线的关键点。你照着这篇文章做应该能比我更快跑通跑通后再根据你的业务场景把 Analyzer 抽出来、接好回调、处理好重复扫码一个能用的扫码模块就成了。本文还有配套的精品资源点击获取

相关新闻

2026/9/2 6:54:13

STM32C5A3R GPIO驱动LED:从硬件原理到模块化代码的嵌入式开发入门

最近在整理一些嵌入式开发的基础项目,发现很多新手在拿到一块开发板后,第一件事就是点亮LED。这看似简单,但真正能把这件事做“透”的人并不多。很多人只是照着教程复制代码,灯亮了就觉得任务完成,却忽略了背后关于硬件…

2026/9/2 7:14:14

用Claude Code搭建农业物联网监测平台:完整实战指南

Claude Code 的 100 个实战案例中,农业物联网监测平台是很有代表性的一种。它并不是让 AI 单纯生成几个页面,而是涉及传感器数据采集、协议解析、存储建模、接口服务、告警计算和可视化展示的完整链路。把这条链路交给 Claude Code 来辅助搭建&#xff0…

2026/9/2 7:14:14

超迷你DC-ATX电源:小机箱空间利用与供电改造完全指南

这次我们来看一个能直接改变小机箱内部布局的电源方案:超迷你 DC-ATX 电源。它的体积往往只有一张名片大小,甚至“手指大小”,却能输出上百瓦功率,把传统 ATX 电源挤出机箱之外。对于折腾 ITX、A4 结构、NAS、软路由、HTPC 的玩家…

2026/9/2 7:14:14

C语言医院挂号系统实战:链表与文件操作的综合应用

简介:本资源是一个基于C语言开发的轻量级医院挂号系统实现,面向C语言初学者与课程设计实践者,旨在通过真实业务场景帮助学习者掌握结构体设计、链表管理、文件持久化及模块化函数开发等核心编程能力。压缩包为ZIP格式,大小56KB&am…

2026/9/2 7:14:14

基于知识图谱与推荐系统的药物靶点预测:从数据到AI模型实战

简介:本资源是一套面向计算机及相关专业本科生的课程设计与期末大作业实战项目,聚焦于生物信息学交叉场景——利用知识图谱与推荐系统协同预测药物-靶点相互作用。项目代码完整、结构清晰,涵盖数据预处理(如hetionet.py、yamanish…

2026/9/1 16:02:17

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/1 8:27:47

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/1 7:04:43

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/2 0:03:41

单片机毕业设计-基于单片机与蓝牙通讯的输液状态监测终端设计与开发 基于 STM32 或 51 单片机的液位‑滴速‑温度多参数输液监护装置设计(024005)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/9/2 0:03:41

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案

这次我们来看一个很实用的 DeepSeek 落地场景:用 DeepSeek 把英文视频字幕自动翻译成中文。具体案例是《恶魔君》1989 年第 28 集的英转中字幕任务,标题写得很直白,但背后其实是一整套可以复用的技术流程:字幕解析、模型调用、批量…

2026/9/2 0:03:41

用Python搭建搞笑语音助手:从语音识别到语音合成全教程

当你家里摆着一台天猫精灵,却总希望语音助手偶尔“不正经”一点,不用官方腔回答问题,而是张口就接几句搞笑段子,会是什么体验?我最近动手验证了一下这个想法——没有去改装任何市面上现有的智能音箱,而是直…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/2 1:15:22

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…