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

相关文章

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可