二维码识别开源项目zxing的使用和源码分析

2024-08-22 18:18

本文主要是介绍二维码识别开源项目zxing的使用和源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引言
最近公司需要用到二维码扫描识别的功能,回去翻看以前的使用,发现搞了很久都没有弄明白。上网搜索更是一堆杂乱的信息,很难去抽离自己需要的信息。于是,狠下心来跟着调用的思路,一步一步的分析源码。最后有种豁然开朗的感觉,哈哈

用法

1.1 添加core-3.0.0.jar

1.2 配置权限

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />

1.3 通过startActivityForResult 启动CaptureActivity

Intent intent = new Intent(MainActivity.this, CaptureActivity.class);
startActivityForResult(intent, REQUEST_CODE_SCAN);

1.4 在onActivityResult中接收回调

分析

1 CaptureActiivity分析

1.1 在onCreate中做了一些初始化工作

1.1.1 设置保持屏幕唤醒状态
1.1.2 创建一个Timer:如果设备使用电池供电,一段时间不活动之后结束activity
1.1.3创建一个BeepManager:主要用于扫描成功后提示声的

1.2 在onResume中

1.2.1 初始化camera:使用CameraManager,这个类主要提供关于camera的一些基本操作
1.2.2 初始化ViewfinderView:覆盖在预览图(SurfaceView)上方的一个view,主要作用是增加了部分透明的取景框和扫描动画;我们可以根据需要改变取景框的大小形状,改变扫描动画等,即可以自定义
1.2.3 初始化SurfaceView

2 CaptureActivityHandler分析

2.1 在初始化camera时,创建了一个CaptureActivityHandler:

private void initCamera(SurfaceHolder surfaceHolder) {if (surfaceHolder == null) {throw new IllegalStateException("No SurfaceHolder provided");}if (cameraManager.isOpen()) {return;}try {// 打开Camera硬件设备cameraManager.openDriver(surfaceHolder);if (handler == null) {handler = new CaptureActivityHandler(this, decodeFormats,decodeHints, characterSet, cameraManager);}} catch (IOException ioe) {Log.w(TAG, ioe);displayFrameworkBugMessageAndExit();} catch (RuntimeException e) {Log.w(TAG, "Unexpected error initializing camera", e);displayFrameworkBugMessageAndExit();}}

2.2 初始化过程分析

public CaptureActivityHandler(CaptureActivity activity,Collection<BarcodeFormat> decodeFormats,Map<DecodeHintType, ?> baseHints, String characterSet,CameraManager cameraManager) {this.activity = activity;decodeThread = new DecodeThread(activity, decodeFormats, baseHints,characterSet, new ViewfinderResultPointCallback(activity.getViewfinderView()));decodeThread.start();state = State.SUCCESS;this.cameraManager = cameraManager;cameraManager.startPreview();restartPreviewAndDecode();}

我们来看下这几句代码的执行:

2.2.1 启动一个DecodeThread:用于解析二维码的子线程,下一节再详细分析

decodeThread = new DecodeThread(activity, decodeFormats, baseHints,characterSet, new ViewfinderResultPointCallback(activity.getViewfinderView()));
decodeThread.start();

2.2.2 cameraManager.startPreview();:开始拍摄预览,内部主要就是调用了camera的startPreview()方法

2.2.3 restartPreviewAndDecode();:开始解码,下边进入这个方法分析

cameraManager.requestPreviewFrame(decodeThread.getHandler(),R.id.decode);我们详细看这个方法的实现

1 previewCallback.setHandler(handler, message);
该PreviewCallback主要实现了Camera.PreviewCallback接口,提供了一个setHandler方法,当回调onPreviewFrame这个方法时,通过设置的Handler来派发消息

2 theCamera.setOneShotPreviewCallback(previewCallback);:利用Camera对象上的这个方法注册Camera.PreviewCallback,从而当下一幅预览图像可用时调用一次onPreviewFrame

3 这两步主要作用就是获取一帧的数据,将这帧数字放在Message中,然后通过decodeThread.getHandler()获取到的handler发送出去;该封装了帧数据的Message包含了一个值为R.id.decode的what(主要用于消息的区分)

3 DecodeThread分析

3.1 DecodeThread继承Thread,是一个专门用于解码的线程

3.2 既然继承Thread,我们可以集中看它的run()方法做了什么操作

@Override
public void run() {Looper.prepare();handler = new DecodeHandler(activity, hints);Log.i("test", "跑进来啦!!");handlerInitLatch.countDown();Looper.loop();
}

创建了一个DecodeHandler,该类才真正实现decode的功能

4 DecodeHandler分析

decodeThread.getHandler()获取到的对象为DecodeHandler,该类继承Handler,主要用于DecodeThread解码线程的消息分发和处理。下边看下消息的处理

@Overridepublic void handleMessage(Message message) {if (!running) {return;}switch (message.what) {case R.id.decode:decode((byte[]) message.obj, message.arg1, message.arg2);break;case R.id.quit:running = false;Looper.myLooper().quit();break;}}

4.1 通过对比message.what,会进入R.id.decode这个分支,这里就是解码真正实现的地方,下边进去decode()这个方法

// 上边是一堆解码的代码,这里不纠结
Handler handler = activity.getHandler();if (rawResult != null) {// Don't log the barcode contents for security.long end = System.currentTimeMillis();Log.d(TAG, "Found barcode in " + (end - start) + " ms");if (handler != null) {Message message = Message.obtain(handler,R.id.decode_succeeded, rawResult);Bundle bundle = new Bundle();bundleThumbnail(source, bundle);message.setData(bundle);message.sendToTarget();}} else {if (handler != null) {Message message = Message.obtain(handler, R.id.decode_failed);message.sendToTarget();}}

4.2 看到解码完成,会通过tivity.getHandler()获取Handler对象,该对象的实例为CaptureActivityHandler,通过它去分发和处理消息,这里会发送两种消息

5重入CaptureActivityHandler

分析消息的处理(handleMessage()方法)

当为R.id.decode_succeeded时,这是解码成功
1 生成一个bitmap
2 通过调用activity.handleDecode将bitmap 和Result对象返回给activity处理
当为R.id. decode_failed时,这是解码失败
继续调用cameraManager.requestPreviewFrame(decodeThread.getHandler(),R.id.decode);方法获取一帧数字,发送给DecodeThread线程解码,不断重复该步骤

6 CountDownLatch使用

6.1 在DecodeThread创建了CountDownLatch:

handlerInitLatch = new CountDownLatch(1);

6.2 分析作用

public Handler getHandler() {try {handlerInitLatch.await();} catch (InterruptedException ie) {// continue?}return handler;
}

6.2.1 可以看到每次调用getHandler()时,都会调用handlerInitLatch.await();:调用此方法会一直阻塞当前线程,直到计时器的值为0;而创建CountDownLatch时,传入的是1,所以计数器的值只会从1-0

6.2.2 那在哪里调用调用了getHandler()方法呢?全局搜索下,有三个地方

1 CaptureActivity的onPause()方法中:主要为了停止消息的派发
2 识别失败、3restartPreviewAndDecode:重新预览识别:
这两个方法都是调用了cameraManager.requestPreviewFrame(decodeThread.getHandler(),R.id.decode);
这是重新扫描
于是,我猜测这里使用CountDownLatch,主要是为了让获取到一帧数据前,先保证DecodeHandler已经被创建,可用于解码

7 总结

其实,二维码识别的功能已经封装好了。对于一些界面的变化,我们可以修改CaptureActivity和ViewfinderView来实现
了解清楚整个流程,更利于我们以后的应用

这篇关于二维码识别开源项目zxing的使用和源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1097026

相关文章

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

IntelliJ IDEA2025创建SpringBoot项目的实现步骤

《IntelliJIDEA2025创建SpringBoot项目的实现步骤》本文主要介绍了IntelliJIDEA2025创建SpringBoot项目的实现步骤,文中通过示例代码介绍的非常详细,对大家... 目录一、创建 Spring Boot 项目1. 新建项目2. 基础配置3. 选择依赖4. 生成项目5.

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件