Android APP: ZXing和ZBar库扫码软件开发

一、需求说明

由于工作需要,已开发一款支持QR Code、Data Matrix和PDF 417的Android扫码软件,在此写几篇随笔。本人原来是做嵌入式C开发的,基本没有面向对象开发的工作经验,因此如果有写的有什么不当之处,欢迎大家指正,小弟在此谢过!

二、需求分析

扫码软件的网络开源库通常用两个:ZXing和ZBar。ZXing是用纯Java开发的库,而ZBar则基于C语言开发。从我个人经验来看,用C开发的库在效率上肯定是高于Java的,所以先找了找关于ZBar的相关说明。

github.com/zbar/zbar上看开源代码的介绍,有如下说明:

It supports EAN-13/UPC-A, UPC-E, EAN-8, Code 128, Code 39, Codabar, Interleaved 2 of 5 and QR Code.

从需求角度来说,ZBar不支持Data Matrix和PDF 417,所以可以否决这个方案。但是有点强迫症的我,想到万一以后需求变化了,或者该库能支持上这两种编码了,到时候再考虑添加进来的话,势必要做很多修改,所以决定再代码上暂时支持,可以不使用。

那么,ZXing库呢?github.com/zxing/zxing上看开源代码介绍,有如下说明:



ZXing能满足要求,必须加上。

三、软件初步设计

既然是扫码软件,必不可少的要加上一个camera模块。一开始我是想用去年挺火的RxJava来实现的,所以找了一下github上的开源库,发现一款github.com/ragnraok/RxCamera库,拿来尝试了一下,功能是能实现了,但是在旋转屏幕的时候会挂掉,只能限制屏幕的旋转,加上我是用RxJava来实现的,而最新的已经是用了RxJava2了(尝试改了一下RxCamera支持RxJava2,确实也能用),加上环境原因,所以最终决定自己写一个简单的RxExecutor(说是Rx,其实并没有链式调用)来实现单线程的camera控制。

其次,由于要同时支持ZXing和ZBar库,所以对这两个库要进行抽象,定为barcode模块。

至此,可以得到5个模块的主要依赖关系如下:


四、开发环境

当然是优先在JDK1.8、Android Studio下开发咯,但是***原因,需要支持另外一个环境,而RaJava2在两种环境中使用会有不同,所以弃用。


五、详细设计

5.1 RxExecutor模块

名字上看,我用了线程池来实现。在本程序中,会出现2种使用场景:单线程的Camera和用来解码的多线程,因此RxExecutor为抽象类。

任务在子线程中执行完毕后调用回调函数,为了减少调用者的工作,提供了内部Handler来实现,保证回调函数在UI线程中运行。

抽象的两个方法如下:

protected abstract ExecutorService createExecutor();

protected abstract Handler createHandler();

提供接口定义如下:

// 单参数返回任务

public interface Func1 {

    T call() throws Exception;

}

// 类型转换任务

public interface Func2 {

    V call(K k) throws Exception;

}

// 观察者角色

public interface Observer {

    void onSuccess(T t);

    void onError(Exception e);

}

// 消费者角色

public interface Consumer {

    void accept(T t);

}

提供外部调用方法如下:

// 执行单参数返回任务

public <T> void operate(final Func1 func, final Observer observer);

public <T> void operate(final Func1 func, final Consumer success);

public <T> void operateDelay(final Func1 func, final Observer observer, long delay);

// 执行类型转换任务

public <V, K> void map(final K k, final Func2 func, final Observer observer);

public <V, K> void map(final K k, final Func2 func, final Consumer success);

public <V, K> void mapDelay(final K k, final Func2 func, final Observer observer, long delay);

5.2 Camera模块

提供参数配置、摄像头任务执行的功能。

5.2.1 CameraConfig

提供参数配置功能。方便起见,提供链式调用,我们可以采用Builder设计模式来实现。

这里初步定下摄像头必配参数:ID、预览分辨率、旋转角度和预览控件。

5.2.2 CameraInstance

提供外部操作摄像头的接口和方法。

接口为获取Preview Frame时的回调:

public interface OneShotCallback {

    void onPreview(int format, int orientation, byte[] data, int width, int height);

}

操作方法包括:

public void openAndStartPreview(final CameraConfig config, final Observer<List<Point> callback);

public void close(final Observer callback);

publicvoid focus(final Observercallback, int delay);

public void getOneShot(final OneShotCallback callback);

public void torch(final boolean isOn, final Observercallback);

public boolean isTorchOn();

publicvoidre setPreviewSize(final Point size, final Observercallback);  // 重设分辨率

5.2.3 其他(略)

5.3 barcode模块

本模块除了对两个库抽象之外,还提供了基本的UI显示功能。

5.3.1 decode子模块

该子模块的作用是对两个库抽象,主要包括结果和解码器抽象。

结果抽象BarcodeResult,目前只做了简单的扫码字符获取。

解码器抽象,两种类型的解码器:Bitmap和YUVImage。

public abstract class Decoder<T> {

    public void decode(RxExecutor.Consumer consumer) {...}  // 外部调用接口

    protected abstract BarcodeResult realDecode();  // 具体库的解码子类实现

}

5.3.2 UI子模块

无它,实现了一个View和一个Activity。

View为ScanBoxView,参考了开源代码github.com/bingoogolapple/BGAQRCode-Android的实现,包括遮罩层、矩形框和提示文字三部分。

Activity采用MVP结构,定义View接口如下:

interface View {

    Contextget Context();

    int getOrientation();  // 获得当前屏幕的实时旋转角度

    void show Progress();

    void hideProgress();

    void showResult(String result);

    void showError(Exception e);

    void showPicNotBarcode();

    void torchBtn(booleanisPressed);  // 手电筒按钮的UI控制

    void createResolutionDialog(List resolutions,Point curResolution);  // 创建预览分辨率对话框(并不显示)

}

定义的Presenter接口如下:

interface Presenter {

    void openCamera(SurfaceView surface);  // 在SurfaceView上打开摄像头

    void closeCamera();  // 关闭摄像头

    void changeResolution(Point resolution);  // 实时更改摄像头分辨率

    void switchTorch();  // 切换手电筒开关状态

    void decodeUriImage(Uri uri);  // 根据图片Uri解码,可以是本地图片,也可以是网络图片,基于Google的开源Glide库实现。

}

5.4 ZXing模块

UI和摄像头控制都已经在Barcode中实现了,因此本模块的任务很简单,实现两个解码器,再具化UI。

Bitmap解码器:Bitmap->LuminanceSource->BinaryBitmap后,使用MultiFormatReader来解码,得到Result,转化为BarcodeResult。

YUVImage解码器:YUVImage->角度旋转->LuminanceSource->BinaryBitmap后,使用MultiFormatReader来解码,得到Result,转化为BarcodeResult。

5.5 ZBar模块

同ZXing,实现两个解码器,都将图片数据转化为Image类型,然后调用ImageScanner来解码,得到SymbolSet结果,转化为BarcodeResult。

六、主要碰到问题记录

6.1 SurfaceHolder

SurfaceHolder的callback设置如果在SurfaceView已经初始化完成,那么会收不到surfaceCreated消息,不掉用surfaceChanged。由于我是在Activity的onResume和onPause中进行开关摄像头操作,所以在Camera模块中必须于UI线程中先调用addCallback,再在子线程中open camera。

6.2 RxJava2

在JDK 1.8上编译通过的代码,到了JDK1.7上编译不过,查了下RxJava2的资料,发现两个版本上的用法有些许区别,需要修改。

6.3 预览分辨率

Camera的预览分辨率是可以支持实时修改的,不必每次都重新open摄像头。

6.4 Orientation

使用OrientationEventListener可以实时监控当前屏幕的旋转角度,可以用来实现锁定屏幕旋转时,横屏也能进行条码识别(主要指一维条码)。

6.5 AlertDialog

发现AlertDialog在某些机型上显示不出来Message,经查由于文字的颜色和默认背景色都为白色,所以没显示出来。


随笔完毕。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,900评论 25 707
  • 二维码扫描最近两年简直是风靡移动互联网时代,尤其在国内发展神速。围绕条码扫码功能,首先说说通过本文你可以知道啥。一...
    55book阅读 4,142评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,605评论 18 399
  • 在人际交往当中,遇物加价,逢人减岁,是人们普遍采用的非常凑效的投其所好的讲话技巧。 首先来说说遇物加价做加法。 买...
    樊荣强阅读 1,917评论 1 5