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

相关文章

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录