二维码识别开源项目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

相关文章

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

使用SQL语言查询多个Excel表格的操作方法

《使用SQL语言查询多个Excel表格的操作方法》本文介绍了如何使用SQL语言查询多个Excel表格,通过将所有Excel表格放入一个.xlsx文件中,并使用pandas和pandasql库进行读取和... 目录如何用SQL语言查询多个Excel表格如何使用sql查询excel内容1. 简介2. 实现思路3

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

c# checked和unchecked关键字的使用

《c#checked和unchecked关键字的使用》C#中的checked关键字用于启用整数运算的溢出检查,可以捕获并抛出System.OverflowException异常,而unchecked... 目录在 C# 中,checked 关键字用于启用整数运算的溢出检查。默认情况下,C# 的整数运算不会自

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W