赞
踩
打造一个干净纯粹的扫码模块;App基本都用到扫码功能(条码、二维码),网上资源一大把,搬过来就能用,确实能用,但应该还能更好用,经过几次与大厂对比,索性自己搞一个;
于是采用ZXing相机预览,ZBar解码整合路线;但是存在几个问题:
Android Studio Electric Eel
操作系统 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
- PATH=$PATH:$HOME/bin
-
- export PATH
- export ANDROID_NDK=/android-ndk-r17c/
- export PATH=$PATH:$ANDROID_NDK
- 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/
tar -xvf libiconv-1.17.tar.gz
因为libiconv不是针对Android的项目,所以我们得修改一下构建脚本,进行;在/androidSrc/libiconv-1.17/目录下创建脚本代码 android_build.sh
- ANDROID_BUILD=/root/my-android-toolchain/
- API_VERSION=28
- PATH=$ANDROID_BUILD/bin:$PATH
- SYSROOT=$ANDROID_BUILD/sysroot
- HOST=arm-linux-androideabi
- CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -D__ANDROID_API__=$API_VERSION"
- CXXFLAGS="-std=c++11"
- LIBDIRS="-L$ANDROID_BUILD/arm-linux-androideabi/lib"
- LDFLAGS="-march=armv7-a -Wl,--fix-cortex-a8 $LIBDIRS"
- CONFLAGS="--prefix=${SYSROOT}/usr --host=$HOST"
- PKG_CONFIG_PATH="$SYSROOT/usr/lib/pkgconfig"
-
- #configure
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" LDFLAGS="$LDFLAGS" ./configure $CONFLAGS &&
-
- #make & install
运行脚本./android_build.sh,等会儿就好了;
Androd.mk
CmakeLists.txt
Android.mk
- MY_LOCAL_PATH := $(call my-dir)
-
- # libiconv
- include $(CLEAR_VARS)
- LOCAL_PATH := $(MY_LOCAL_PATH)
- LOCAL_MODULE := libiconv
- LOCAL_CFLAGS := \
- -Wno-multichar \
- -D_ANDROID \
- -DLIBDIR="c" \
- -DBUILDING_LIBICONV \
- -DBUILDING_LIBCHARSET \
- -DIN_LIBRARY
-
- LOCAL_SRC_FILES := \
- libiconv-1.17/lib/iconv.c \
- libiconv-1.17/libcharset/lib/localcharset.c \
- libiconv-1.17/lib/relocatable.c
-
- LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/libiconv-1.17/include \
- $(LOCAL_PATH)/libiconv-1.17/libcharset \
- $(LOCAL_PATH)/libiconv-1.17/libcharset/include
-
- include $(BUILD_SHARED_LIBRARY)
-
- LOCAL_LDLIBS := -llog -lcharset
-
- # -----------------------------------------------------
-
- # libzbar
- include $(CLEAR_VARS)
- LOCAL_PATH := $(MY_LOCAL_PATH)
- LOCAL_MODULE := zbar
- LOCAL_SRC_FILES := \
- zbarjni.c \
- zbar/img_scanner.c \
- zbar/decoder.c \
- zbar/image.c \
- zbar/symbol.c \
- zbar/convert.c \
- zbar/config.c \
- zbar/scanner.c \
- zbar/error.c \
- zbar/refcnt.c \
- zbar/video.c \
- zbar/video/null.c \
- zbar/decoder/code128.c \
- zbar/decoder/code39.c \
- zbar/decoder/code93.c \
- zbar/decoder/codabar.c \
- zbar/decoder/databar.c \
- zbar/decoder/ean.c \
- zbar/decoder/i25.c \
- zbar/decoder/qr_finder.c \
- zbar/qrcode/bch15_5.c \
- zbar/qrcode/binarize.c \
- zbar/qrcode/isaac.c \
- zbar/qrcode/qrdec.c \
- zbar/qrcode/qrdectxt.c \
- zbar/qrcode/rs.c \
- zbar/qrcode/util.c
-
- LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/zbar \
- $(LOCAL_PATH)/libiconv-1.17/include
-
- LOCAL_SHARED_LIBRARIES := libiconv
-
- include $(BUILD_SHARED_LIBRARY)
CMakeLists.txt
- # Set the minimum version of CMake required
- cmake_minimum_required(VERSION 3.4.1)
-
- # Set the project name and version
- project(zbarjni VERSION 1.0)
-
- # Enable C++11 support
- set(CMAKE_CXX_STANDARD 11)
- set(CMAKE_CXX_STANDARD_REQUIRED True)
- set(JNI_SOURCE ${CMAKE_SOURCE_DIR}/src/main/jni)
- # Include directories
- include_directories(${JNI_SOURCE}
- ${JNI_SOURCE}/include
- ${JNI_SOURCE}/libiconv-1.17/libcharset
- ${JNI_SOURCE}/libiconv-1.17/include
- ${JNI_SOURCE}/libiconv-1.17/libcharset/include
- ${JNI_SOURCE}/zbar)
-
- # libiconv library
- add_library(libiconv SHARED
- ${JNI_SOURCE}/libiconv-1.17/lib/iconv.c
- ${JNI_SOURCE}/libiconv-1.17/libcharset/lib/localcharset.c
- ${JNI_SOURCE}/libiconv-1.17/lib/relocatable.c)
-
- target_compile_options(libiconv PRIVATE
- -Wno-multichar
- -D_ANDROID
- -DLIBDIR="c"
- -DBUILDING_LIBICONV
- -DBUILDING_LIBCHARSET
- -DIN_LIBRARY)
-
- # zbar library
- add_library(zbarjni SHARED
- ${JNI_SOURCE}/zbarjni.c
- ${JNI_SOURCE}/zbar/img_scanner.c
- ${JNI_SOURCE}/zbar/decoder.c
- ${JNI_SOURCE}/zbar/image.c
- ${JNI_SOURCE}/zbar/symbol.c
- ${JNI_SOURCE}/zbar/convert.c
- ${JNI_SOURCE}/zbar/config.c
- ${JNI_SOURCE}/zbar/scanner.c
- ${JNI_SOURCE}/zbar/error.c
- ${JNI_SOURCE}/zbar/refcnt.c
- ${JNI_SOURCE}/zbar/video.c
- ${JNI_SOURCE}/zbar/video/null.c
- ${JNI_SOURCE}/zbar/decoder/code128.c
- ${JNI_SOURCE}/zbar/decoder/code39.c
- ${JNI_SOURCE}/zbar/decoder/code93.c
- ${JNI_SOURCE}/zbar/decoder/codabar.c
- ${JNI_SOURCE}/zbar/decoder/databar.c
- ${JNI_SOURCE}/zbar/decoder/ean.c
- ${JNI_SOURCE}/zbar/decoder/i25.c
- ${JNI_SOURCE}/zbar/decoder/qr_finder.c
- ${JNI_SOURCE}/zbar/qrcode/bch15_5.c
- ${JNI_SOURCE}/zbar/qrcode/binarize.c
- ${JNI_SOURCE}/zbar/qrcode/isaac.c
- ${JNI_SOURCE}/zbar/qrcode/qrdec.c
- ${JNI_SOURCE}/zbar/qrcode/qrdectxt.c
- ${JNI_SOURCE}/zbar/qrcode/rs.c
- ${JNI_SOURCE}/zbar/qrcode/util.c)
-
- target_link_libraries(zbarjni PRIVATE libiconv log)
-
- # Link against Android log and charset libraries
- #target_link_libraries(zbar log charset)
-
- # Enable verbose build output (optional)
- set(CMAKE_VERBOSE_MAKEFILE ON)
-
- # Set the default build type (optional)
- set(CMAKE_BUILD_TYPE Release)
-
- # If you have subdirectories with more CMakeLists.txt files, you can add them here
- # add_subdirectory(subdirectory_name)
-
- # Finish the CMakeLists.txt file
到这里配置结束,开始编译;理论上是没问题的;
导入java类,ZBar-master\java\net\sourceforge\zbar所有类都cp到module来,注意:包名不要改,这边和zbarjni.c对应着;
Zxingdemo比较多,这边只做扫码,所以简化一下导入;
添加依赖
- api 'com.google.zxing:core:3.3.3'
- api 'com.google.zxing:android-core:3.3.0'
导入java类
导入资源文件
错误提示一个个处理,调用 PreferenceManager.getDefaultSharedPreferences,就全部改为取默认值;
注意:因为是library模块,所以switch(id)得改成用if判断;
完事就构建;
ZXing默认横屏改为竖屏;
AndroidManifest.xml-activity -CaptureActivity, android:screenOrientation="portrait"
CameraManager里的 getFramingRectInpreview()方法,增加cameraResolution的判断
- if(cameraResolution.x>cameraResolution.y){ //x大于y 改成竖屏数据,y和x互换 计算截图
-
- rect.left = rect.left * cameraResolution.y / screenResolution.x;
- rect.right = rect.right * cameraResolution.y / screenResolution.x;
- rect.top = rect.top * cameraResolution.x / screenResolution.y;
- rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
-
- }else{
-
- rect.left = rect.left * cameraResolution.x / screenResolution.x;
- rect.right = rect.right * cameraResolution.x / screenResolution.x;
- rect.top = rect.top * cameraResolution.y / screenResolution.y;
- rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
- }
解码部分,DecodeHandler 的decode数据进行矩阵替换,宽高互换;这边改用c进行转换;代码如下
- JNIEXPORT jbyteArray JNICALL Java_net_sourceforge_zbar_DecodeManager_dataHandler
- (JNIEnv *env, jobject thiz, jbyteArray array, jint length, jint width, jint height){
-
-
- jbyte* jBuffer = (*env) -> GetByteArrayElements(env, array, 0);
- unsigned char* pbuffer = (unsigned char*)jBuffer;
-
-
-
-
- jbyteArray resultArray = (*env) -> NewByteArray(env, length);
- jbyte *bytes = (*env) -> GetByteArrayElements(env,resultArray, 0);
-
- for(int y=0; y<height; y++){
- for(int x=0; x<width; x++){
- bytes[x * height + height - y - 1] = pbuffer[x + y * width];
- }
- }
-
-
-
- (*env) -> SetByteArrayRegion(env, resultArray, 0, length, bytes);
-
- (*env) -> ReleaseByteArrayElements(env, array, jBuffer, JNI_ABORT); //释放
- (*env) -> ReleaseByteArrayElements(env, resultArray, bytes, JNI_ABORT); //释放
-
- return resultArray;
- }
net.sourceforge.zbar.DecodeManager.java
- public class DecodeManager extends ImageScanner {
- public static native byte[] dataHandler(byte[] by, int length, int width, int height);
- }
DecodeHandler 的decode方法,调用
data = decodeManager.dataHandler(data, data.length, width, height)
考虑Zbar部分条码类型不支持,解码部分先尝试Zbar,如果失败在用ZXing;
完整decode方法代码:
- private void decode(byte[] data, int width, int height) {
- String resultQRcode = null;
- Result rawResult = null;
- // 先用zbar解码 如果失败或者识别不了用zxing
- Image barcode = new Image(width, height, "Y800");
- barcode.setData(data);
- Rect rect = activity.getCameraManager().getFramingRectInPreview();
- if (rect != null) {
- /*
- zbar 解码库,不需要将数据进行旋转,因此设置裁剪区域是的x为 top, y为left
- 设置了裁剪区域,解码速度快了近5倍左右
- */
- barcode.setCrop(rect.top, rect.left, rect.width(), rect.height()); // 设置截取区域,也就是你的扫描框在图片上的区域.
- }
- ImageScanner mImageScanner = new ImageScanner();
- int result = mImageScanner.scanImage(barcode);
- if (result != 0) {
- SymbolSet symSet = mImageScanner.getResults();
- for (Symbol sym : symSet) {
- resultQRcode = sym.getData();
- BarcodeFormat str = sym.getSymbolName();//条码格式,转化一下统一结果
- rawResult = new Result(resultQRcode, data, null, str);
- Log.d(getClass().getName(),resultQRcode);
- }
-
- }
- if (TextUtils.isEmpty(resultQRcode) || rawResult == null) { //如果扫描模式是Zxing
- /*
- 因为相机传感器捕获的数据是横向的, 所以需要将数据进行90度的旋转, 用java进行转换在红米三手机测试大概需要 600ms左右
- 因此换了C语言, 只需要 35ms左右 速度快了接近 20倍
- */
- data = decodeManager.dataHandler(data, data.length, width, height);
- //Log.d(TAG, "数组转换用时: " + (System.currentTimeMillis() - start));
- int tmp = width;
- width = height;
- height = tmp;
-
-
- PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
- if (source != null) {
- BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
- try {
- rawResult = multiFormatReader.decodeWithState(bitmap);
- } catch (ReaderException re) {
- // continue
- } finally {
- multiFormatReader.reset();
- }
- }
- if (rawResult != null) {
- resultQRcode = rawResult.getText();
- }
-
- }
-
- long end = System.currentTimeMillis();
-
-
- Handler handler = activity.getHandler();
- if (!TextUtils.isEmpty(resultQRcode)) { // 非空表示识别出结果了。
- if (handler != null) {
- Log.d(getClass().getName(), "解码成功: " + resultQRcode);
- Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
- message.sendToTarget();
- }
- } else {
- if (handler != null) {
- Message message = Message.obtain(handler, R.id.decode_failed);
- message.sendToTarget();
- }
- }
-
- }
ZBar中文乱码,修改ZBar源码,src/main/jni/zbar/qrcode/qrdectxt.c在63行处,修改如下
- // latin1_cd=iconv_open("UTF-8","ISO8859-1");
- // sjis_cd=iconv_open("UTF-8","SJIS");
- //中文乱码
- latin1_cd=iconv_open("UTF-8","GB18030");
-
- sjis_cd=iconv_open("UTF-8","GB2312");
到这里就结束了,可以依赖到项目中去愉快扫码了;
界面优化,加入闪光灯,运行时权限参考:Android集成zxing扫码框架
编译参考:Linux下搭建Android交叉编译环境;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。