当前位置:   article > 正文

Android扫一扫(ZXing与ZBar整合)_android zbar

android zbar

需求

打造一个干净纯粹的扫码模块;App基本都用到扫码功能(条码、二维码),网上资源一大把,搬过来就能用,确实能用,但应该还能更好用,经过几次与大厂对比,索性自己搞一个;

于是采用ZXing相机预览,ZBar解码整合路线;但是存在几个问题:

  1. ZBar中文乱码
  2. ZBar支持的条码类型有限,部分ZXing支持的格式,ZBar不支持;
  3. ZXing默认是横屏扫码

技术

  • Linux下NDK编译
  • Android jni
  • Android Studio 配置CMakeLists

资料

源码

编译工具

android-ndk-r17c-linux-x86_64

交叉编译

Android Studio Electric Eel

Linux环境搭建

操作系统 centos7

Linux环境搭建

解压Linux NDK工具到/android-ndk-r17c/

解压叫餐编译工具到/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/

配置环境变量vi ~/.bash_profile

更新环境变量source ~/.bash_profile

  1. PATH=$PATH:$HOME/bin
  2. export PATH
  3. export ANDROID_NDK=/android-ndk-r17c/
  4. export PATH=$PATH:$ANDROID_NDK
  5. export PATH=/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin:$PATH

进入/android-ndk-r17c/build/tools,运行命令

./make_standalone_toolchain.py --arch arm --api 28 --unified-headers --install-dir /root/my-android-toolchain/

1、编译libiconv

  1. 下载libiconv源码,上传到Linux,解压;

tar -xvf libiconv-1.17.tar.gz

因为libiconv不是针对Android的项目,所以我们得修改一下构建脚本,进行;在/androidSrc/libiconv-1.17/目录下创建脚本代码 android_build.sh

  1. ANDROID_BUILD=/root/my-android-toolchain/
  2. API_VERSION=28
  3. PATH=$ANDROID_BUILD/bin:$PATH
  4. SYSROOT=$ANDROID_BUILD/sysroot
  5. HOST=arm-linux-androideabi
  6. CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -D__ANDROID_API__=$API_VERSION"
  7. CXXFLAGS="-std=c++11"
  8. LIBDIRS="-L$ANDROID_BUILD/arm-linux-androideabi/lib"
  9. LDFLAGS="-march=armv7-a -Wl,--fix-cortex-a8 $LIBDIRS"
  10. CONFLAGS="--prefix=${SYSROOT}/usr --host=$HOST"
  11. PKG_CONFIG_PATH="$SYSROOT/usr/lib/pkgconfig"
  12. #configure
  13. PKG_CONFIG_PATH=$PKG_CONFIG_PATH CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" LDFLAGS="$LDFLAGS" ./configure $CONFLAGS &&
  14. #make & install

运行脚本./android_build.sh,等会儿就好了;

2、编译ZBar

  • 在android studio创建一个library模块,方便项目可以直接引入使用;
  • 在src/main下创建jni文件夹用于存放C源码,如果创建module有jni目录可跳过;
  • 复制ZBar源码到jni下

  • 复制ZBar-master\android\jni\config.h到src/main/jni下;
  • 复制ZBar-master\java\zbarjni.c到src/main/jni下;
  • 复制刚刚Linux构建完的libiconv源码到jni中

  • ZBar官方有提供了Androd.mk,也可以直接使用它,需要做一下修改,ndk-build;另一种是cmake,在ZBar模块路径下创建CmakeLists.txt用于构建jni;采用一种就可以,需要在build.gradle做配置android.defaultConfig.externalNativeBuild和android.externalNativeBuild

Androd.mk

CmakeLists.txt

Android.mk

  1. MY_LOCAL_PATH := $(call my-dir)
  2. # libiconv
  3. include $(CLEAR_VARS)
  4. LOCAL_PATH := $(MY_LOCAL_PATH)
  5. LOCAL_MODULE := libiconv
  6. LOCAL_CFLAGS := \
  7. -Wno-multichar \
  8. -D_ANDROID \
  9. -DLIBDIR="c" \
  10. -DBUILDING_LIBICONV \
  11. -DBUILDING_LIBCHARSET \
  12. -DIN_LIBRARY
  13. LOCAL_SRC_FILES := \
  14. libiconv-1.17/lib/iconv.c \
  15. libiconv-1.17/libcharset/lib/localcharset.c \
  16. libiconv-1.17/lib/relocatable.c
  17. LOCAL_C_INCLUDES := \
  18. $(LOCAL_PATH)/libiconv-1.17/include \
  19. $(LOCAL_PATH)/libiconv-1.17/libcharset \
  20. $(LOCAL_PATH)/libiconv-1.17/libcharset/include
  21. include $(BUILD_SHARED_LIBRARY)
  22. LOCAL_LDLIBS := -llog -lcharset
  23. # -----------------------------------------------------
  24. # libzbar
  25. include $(CLEAR_VARS)
  26. LOCAL_PATH := $(MY_LOCAL_PATH)
  27. LOCAL_MODULE := zbar
  28. LOCAL_SRC_FILES := \
  29. zbarjni.c \
  30. zbar/img_scanner.c \
  31. zbar/decoder.c \
  32. zbar/image.c \
  33. zbar/symbol.c \
  34. zbar/convert.c \
  35. zbar/config.c \
  36. zbar/scanner.c \
  37. zbar/error.c \
  38. zbar/refcnt.c \
  39. zbar/video.c \
  40. zbar/video/null.c \
  41. zbar/decoder/code128.c \
  42. zbar/decoder/code39.c \
  43. zbar/decoder/code93.c \
  44. zbar/decoder/codabar.c \
  45. zbar/decoder/databar.c \
  46. zbar/decoder/ean.c \
  47. zbar/decoder/i25.c \
  48. zbar/decoder/qr_finder.c \
  49. zbar/qrcode/bch15_5.c \
  50. zbar/qrcode/binarize.c \
  51. zbar/qrcode/isaac.c \
  52. zbar/qrcode/qrdec.c \
  53. zbar/qrcode/qrdectxt.c \
  54. zbar/qrcode/rs.c \
  55. zbar/qrcode/util.c
  56. LOCAL_C_INCLUDES := \
  57. $(LOCAL_PATH)/include \
  58. $(LOCAL_PATH)/zbar \
  59. $(LOCAL_PATH)/libiconv-1.17/include
  60. LOCAL_SHARED_LIBRARIES := libiconv
  61. include $(BUILD_SHARED_LIBRARY)

CMakeLists.txt

  1. # Set the minimum version of CMake required
  2. cmake_minimum_required(VERSION 3.4.1)
  3. # Set the project name and version
  4. project(zbarjni VERSION 1.0)
  5. # Enable C++11 support
  6. set(CMAKE_CXX_STANDARD 11)
  7. set(CMAKE_CXX_STANDARD_REQUIRED True)
  8. set(JNI_SOURCE ${CMAKE_SOURCE_DIR}/src/main/jni)
  9. # Include directories
  10. include_directories(${JNI_SOURCE}
  11. ${JNI_SOURCE}/include
  12. ${JNI_SOURCE}/libiconv-1.17/libcharset
  13. ${JNI_SOURCE}/libiconv-1.17/include
  14. ${JNI_SOURCE}/libiconv-1.17/libcharset/include
  15. ${JNI_SOURCE}/zbar)
  16. # libiconv library
  17. add_library(libiconv SHARED
  18. ${JNI_SOURCE}/libiconv-1.17/lib/iconv.c
  19. ${JNI_SOURCE}/libiconv-1.17/libcharset/lib/localcharset.c
  20. ${JNI_SOURCE}/libiconv-1.17/lib/relocatable.c)
  21. target_compile_options(libiconv PRIVATE
  22. -Wno-multichar
  23. -D_ANDROID
  24. -DLIBDIR="c"
  25. -DBUILDING_LIBICONV
  26. -DBUILDING_LIBCHARSET
  27. -DIN_LIBRARY)
  28. # zbar library
  29. add_library(zbarjni SHARED
  30. ${JNI_SOURCE}/zbarjni.c
  31. ${JNI_SOURCE}/zbar/img_scanner.c
  32. ${JNI_SOURCE}/zbar/decoder.c
  33. ${JNI_SOURCE}/zbar/image.c
  34. ${JNI_SOURCE}/zbar/symbol.c
  35. ${JNI_SOURCE}/zbar/convert.c
  36. ${JNI_SOURCE}/zbar/config.c
  37. ${JNI_SOURCE}/zbar/scanner.c
  38. ${JNI_SOURCE}/zbar/error.c
  39. ${JNI_SOURCE}/zbar/refcnt.c
  40. ${JNI_SOURCE}/zbar/video.c
  41. ${JNI_SOURCE}/zbar/video/null.c
  42. ${JNI_SOURCE}/zbar/decoder/code128.c
  43. ${JNI_SOURCE}/zbar/decoder/code39.c
  44. ${JNI_SOURCE}/zbar/decoder/code93.c
  45. ${JNI_SOURCE}/zbar/decoder/codabar.c
  46. ${JNI_SOURCE}/zbar/decoder/databar.c
  47. ${JNI_SOURCE}/zbar/decoder/ean.c
  48. ${JNI_SOURCE}/zbar/decoder/i25.c
  49. ${JNI_SOURCE}/zbar/decoder/qr_finder.c
  50. ${JNI_SOURCE}/zbar/qrcode/bch15_5.c
  51. ${JNI_SOURCE}/zbar/qrcode/binarize.c
  52. ${JNI_SOURCE}/zbar/qrcode/isaac.c
  53. ${JNI_SOURCE}/zbar/qrcode/qrdec.c
  54. ${JNI_SOURCE}/zbar/qrcode/qrdectxt.c
  55. ${JNI_SOURCE}/zbar/qrcode/rs.c
  56. ${JNI_SOURCE}/zbar/qrcode/util.c)
  57. target_link_libraries(zbarjni PRIVATE libiconv log)
  58. # Link against Android log and charset libraries
  59. #target_link_libraries(zbar log charset)
  60. # Enable verbose build output (optional)
  61. set(CMAKE_VERBOSE_MAKEFILE ON)
  62. # Set the default build type (optional)
  63. set(CMAKE_BUILD_TYPE Release)
  64. # If you have subdirectories with more CMakeLists.txt files, you can add them here
  65. # add_subdirectory(subdirectory_name)
  66. # Finish the CMakeLists.txt file

到这里配置结束,开始编译;理论上是没问题的;

导入java类,ZBar-master\java\net\sourceforge\zbar所有类都cp到module来,注意:包名不要改,这边和zbarjni.c对应着;

3、引入ZXing

Zxingdemo比较多,这边只做扫码,所以简化一下导入;

添加依赖

  1. api 'com.google.zxing:core:3.3.3'
  2. api 'com.google.zxing:android-core:3.3.0'

导入java类

导入资源文件

错误提示一个个处理,调用 PreferenceManager.getDefaultSharedPreferences,就全部改为取默认值;

注意:因为是library模块,所以switch(id)得改成用if判断;

完事就构建;

4、修复

ZXing默认横屏改为竖屏;

AndroidManifest.xml-activity -CaptureActivity, android:screenOrientation="portrait"

CameraManager里的 getFramingRectInpreview()方法,增加cameraResolution的判断

  1. if(cameraResolution.x>cameraResolution.y){ //x大于y 改成竖屏数据,y和x互换 计算截图
  2. rect.left = rect.left * cameraResolution.y / screenResolution.x;
  3. rect.right = rect.right * cameraResolution.y / screenResolution.x;
  4. rect.top = rect.top * cameraResolution.x / screenResolution.y;
  5. rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
  6. }else{
  7. rect.left = rect.left * cameraResolution.x / screenResolution.x;
  8. rect.right = rect.right * cameraResolution.x / screenResolution.x;
  9. rect.top = rect.top * cameraResolution.y / screenResolution.y;
  10. rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
  11. }

解码部分,DecodeHandler 的decode数据进行矩阵替换,宽高互换;这边改用c进行转换;代码如下

  1. JNIEXPORT jbyteArray JNICALL Java_net_sourceforge_zbar_DecodeManager_dataHandler
  2. (JNIEnv *env, jobject thiz, jbyteArray array, jint length, jint width, jint height){
  3. jbyte* jBuffer = (*env) -> GetByteArrayElements(env, array, 0);
  4. unsigned char* pbuffer = (unsigned char*)jBuffer;
  5. jbyteArray resultArray = (*env) -> NewByteArray(env, length);
  6. jbyte *bytes = (*env) -> GetByteArrayElements(env,resultArray, 0);
  7. for(int y=0; y<height; y++){
  8. for(int x=0; x<width; x++){
  9. bytes[x * height + height - y - 1] = pbuffer[x + y * width];
  10. }
  11. }
  12. (*env) -> SetByteArrayRegion(env, resultArray, 0, length, bytes);
  13. (*env) -> ReleaseByteArrayElements(env, array, jBuffer, JNI_ABORT); //释放
  14. (*env) -> ReleaseByteArrayElements(env, resultArray, bytes, JNI_ABORT); //释放
  15. return resultArray;
  16. }

net.sourceforge.zbar.DecodeManager.java

  1. public class DecodeManager extends ImageScanner {
  2. public static native byte[] dataHandler(byte[] by, int length, int width, int height);
  3. }

DecodeHandler 的decode方法,调用

data = decodeManager.dataHandler(data, data.length, width, height)

考虑Zbar部分条码类型不支持,解码部分先尝试Zbar,如果失败在用ZXing;

完整decode方法代码:

  1. private void decode(byte[] data, int width, int height) {
  2. String resultQRcode = null;
  3. Result rawResult = null;
  4. // 先用zbar解码 如果失败或者识别不了用zxing
  5. Image barcode = new Image(width, height, "Y800");
  6. barcode.setData(data);
  7. Rect rect = activity.getCameraManager().getFramingRectInPreview();
  8. if (rect != null) {
  9. /*
  10. zbar 解码库,不需要将数据进行旋转,因此设置裁剪区域是的x为 top, y为left
  11. 设置了裁剪区域,解码速度快了近5倍左右
  12. */
  13. barcode.setCrop(rect.top, rect.left, rect.width(), rect.height()); // 设置截取区域,也就是你的扫描框在图片上的区域.
  14. }
  15. ImageScanner mImageScanner = new ImageScanner();
  16. int result = mImageScanner.scanImage(barcode);
  17. if (result != 0) {
  18. SymbolSet symSet = mImageScanner.getResults();
  19. for (Symbol sym : symSet) {
  20. resultQRcode = sym.getData();
  21. BarcodeFormat str = sym.getSymbolName();//条码格式,转化一下统一结果
  22. rawResult = new Result(resultQRcode, data, null, str);
  23. Log.d(getClass().getName(),resultQRcode);
  24. }
  25. }
  26. if (TextUtils.isEmpty(resultQRcode) || rawResult == null) { //如果扫描模式是Zxing
  27. /*
  28. 因为相机传感器捕获的数据是横向的, 所以需要将数据进行90度的旋转, 用java进行转换在红米三手机测试大概需要 600ms左右
  29. 因此换了C语言, 只需要 35ms左右 速度快了接近 20
  30. */
  31. data = decodeManager.dataHandler(data, data.length, width, height);
  32. //Log.d(TAG, "数组转换用时: " + (System.currentTimeMillis() - start));
  33. int tmp = width;
  34. width = height;
  35. height = tmp;
  36. PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
  37. if (source != null) {
  38. BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
  39. try {
  40. rawResult = multiFormatReader.decodeWithState(bitmap);
  41. } catch (ReaderException re) {
  42. // continue
  43. } finally {
  44. multiFormatReader.reset();
  45. }
  46. }
  47. if (rawResult != null) {
  48. resultQRcode = rawResult.getText();
  49. }
  50. }
  51. long end = System.currentTimeMillis();
  52. Handler handler = activity.getHandler();
  53. if (!TextUtils.isEmpty(resultQRcode)) { // 非空表示识别出结果了。
  54. if (handler != null) {
  55. Log.d(getClass().getName(), "解码成功: " + resultQRcode);
  56. Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
  57. message.sendToTarget();
  58. }
  59. } else {
  60. if (handler != null) {
  61. Message message = Message.obtain(handler, R.id.decode_failed);
  62. message.sendToTarget();
  63. }
  64. }
  65. }

ZBar中文乱码,修改ZBar源码,src/main/jni/zbar/qrcode/qrdectxt.c在63行处,修改如下

  1. // latin1_cd=iconv_open("UTF-8","ISO8859-1");
  2. // sjis_cd=iconv_open("UTF-8","SJIS");
  3. //中文乱码
  4. latin1_cd=iconv_open("UTF-8","GB18030");
  5. sjis_cd=iconv_open("UTF-8","GB2312");

到这里就结束了,可以依赖到项目中去愉快扫码了;

5、优化

界面优化,加入闪光灯,运行时权限参考:Android集成zxing扫码框架

编译参考:Linux下搭建Android交叉编译环境

源码地址

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/949544
推荐阅读
相关标签
  

闽ICP备14008679号