通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(一)

在这个二维码满天飞的社会里,似乎每个app都会拥有自己的扫码功能模块,大多数公司都没法像微信一样研究自己的一套扫码库,从而采用第三方开源库来使用,在安卓开源库里,使用最多的就是

Zbar
Zxing

本文通过分析Zxing 官网给出的android 样例工程,来讲解如何使用Zxing这个开源库。

Zxing demo工程导入

第一步,进入github https://github.com/zxing/zxing 下载demo代码,github上代码有两种方式,一种是通过 git clone ,一种是直接下载到桌面,这里采用直接下载到桌面方式。

image.png

android 需要导入的只是
image.png

这个目录是一个 android完整的工程(Eclipse),用android studio 直接导入即可
file->New->import project 各种next
工程导入后,就会出现一堆错误,放心,都是gradle 配置的问题,通过配置gradle 都能解决。
根目录下的build.gradle 添加如下:

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
    }
}
allprojects {
    repositories {
        jcenter()
        google()
    }
}

app下的build.gradle添加如下代码(我当时也不知道要怎么弄,但是代码里报缺这些类的错误,就加上这些依赖,结果还真成功了,而且这个版本号很重要,版本号低了,有一些方法就缺失了)

dependencies {
        implementation 'com.google.zxing:core:3.3.2'
        implementation 'com.google.zxing:android-core:3.3.0'

    }

这样整个工程就能跑起来了,代码结构如下


image.png

我们跑一下程序,看看这个程序长什么样。


image.png

Google 爸爸特别实在,demo里提供了各种设置,点开右上角设置里面,各种历史记录,各种形式的码,Wi-Fi设置等,我们这里主要通过首页分析,扫码API的调用,一些偏门设置可以自行研究。
我们主要需要关注这几个类就好了

CaptureActivity ,ViewfinderView,CaptureActivityHandler,DecodeHandler,DecodeThread

整个流程大概就是这样子滴,CaptureActivity中嵌套ViewfinderView,然后通过CamerManager拍摄到的二维码图片通过CaptureAcitivityHandler 传给DecodeThread处理,DecodeThread处理完结果又通过DecodeHandler回传给CaptureActivityHandler处理,最终在CaptureActivity进行展示。

关键代码分析,看注释

CaptureActivity.class
大多数处理都在 onResume 和onPause中处理,onResume中对camera 进行初始化,处理google 自己定义的一系列标准的Intent。

private void initCamera(SurfaceHolder surfaceHolder) {
    if (surfaceHolder == null) {
      throw new IllegalStateException("No SurfaceHolder provided");
    }
    if (cameraManager.isOpen()) {
      Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
      return;
    }
    try {
      //打开相机
      cameraManager.openDriver(surfaceHolder);
      // Creating the handler starts the preview, which can also throw a RuntimeException.
      if (handler == null) {
        //将相机等参数在此传给CaptureActivityHandler captureActivityHandler 处理后会 回调CaptureActivity的
        // handleDecode()方法 handleDecode方法得到 解析结果 然后做自己的处理
        //decodeFomats:支持扫码的类型
        handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
      }
      decodeOrStoreSavedBitmap(null, null);
    } catch (IOException ioe) {
      Log.w(TAG, ioe);
      displayFrameworkBugMessageAndExit();
    } catch (RuntimeException e) {
      // Barcode Scanner has seen crashes in the wild of this variety:
      // java.?lang.?RuntimeException: Fail to connect to camera service
      Log.w(TAG, "Unexpected error initializing camera", e);
      displayFrameworkBugMessageAndExit();
    }
  }

CaptureActivityHandler.class 该类将耗时的解析过程交给 DecodeThread处理

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())); //此处传入的viewFinderview 会拿到 扫描的像素信息
    decodeThread.start();//处理camera 抓取到的图像像素信息
    state = State.SUCCESS;

    // Start ourselves capturing previews and decoding.
    this.cameraManager = cameraManager;
    cameraManager.startPreview();
    restartPreviewAndDecode();
  }
 @Override
  public void handleMessage(Message message) {
    switch (message.what) {
      case R.id.restart_preview:
        restartPreviewAndDecode();
        break;
      case R.id.decode_succeeded:
        state = State.SUCCESS;
        Bundle bundle = message.getData();
        Bitmap barcode = null;
        float scaleFactor = 1.0f;
        if (bundle != null) {
          byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
          if (compressedBitmap != null) {
            barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
            // Mutable copy:
            barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
          }
          scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);          
        }
        //处理成功后 回调CaptureActivity的handleDecode方法
        activity.handleDecode((Result) message.obj, barcode, scaleFactor); 
        break;
      case R.id.decode_failed:
        // We're decoding as fast as possible, so when one decode fails, start another.
        state = State.PREVIEW;
       //给CameraManager 传DecodeHandler  CameraManager通过
      //DecoderHandler 将扫描的像素信息传给DecoderHandler处理
        cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
        break;

DecodeThread.class

@Override
  public void run() {
    Looper.prepare();
    //让DecodeHander处理 camera扫描的信息
    handler = new DecodeHandler(activity, hints);
    handlerInitLatch.countDown();
    Looper.loop();
  }

DecodeHandler.classs 该类拿到 Camera扫描的数据 进行分析,将结果返回给CaptureActivityHandler


  DecodeHandler(CaptureActivity activity, Map<DecodeHintType,Object> hints) {
    multiFormatReader = new MultiFormatReader(); //zxing 提供的解析数据的类
    multiFormatReader.setHints(hints); //需要解析的二维码类型
    this.activity = activity;
  }

  @Override
  public void handleMessage(Message message) {
    if (message == null || !running) {
      return;
    }
    switch (message.what) {
      case R.id.decode:  //camera 传来的message 进行解析
        decode((byte[]) message.obj, message.arg1, message.arg2);
        break;
      case R.id.quit:
        running = false;
        Looper.myLooper().quit();
        break;
    }
  }

  /**
   * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
   * reuse the same reader objects from one decode to the next.
   *
   * @param data   The YUV preview frame.
   * @param width  The width of the preview frame.
   * @param height The height of the preview frame.
   */
  private void decode(byte[] data, int width, int height) {
    long start = System.currentTimeMillis();
    Result rawResult = null;
    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();
      }
    }
   //处理结果 返回给CaptureActivityHandler处理
    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();
      }
    }
  }

CameraManager.class 相机相关的处理 接收DecodeHandler 通过decodeHandler发消息返回 camera扫描得到的数据在DecodeHandler里处理

public synchronized void requestPreviewFrame(Handler handler, int message) {
   OpenCamera theCamera = camera;
   if (theCamera != null && previewing) {
     previewCallback.setHandler(handler, message);
     theCamera.getCamera().setOneShotPreviewCallback(previewCallback);
   }
 }
void setHandler(Handler previewHandler, int previewMessage) {
    this.previewHandler = previewHandler;
    this.previewMessage = previewMessage;
  }

  @Override
  public void onPreviewFrame(byte[] data, Camera camera) {
    Point cameraResolution = configManager.getCameraResolution();
    Handler thePreviewHandler = previewHandler;
    if (cameraResolution != null && thePreviewHandler != null) {
      //传给DecodeHandler处理的数据
      Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
          cameraResolution.y, data);
      message.sendToTarget();
      previewHandler = null;
    } else {
      Log.d(TAG, "Got preview callback, but no handler or resolution available");
    }
  }

到此整个扫描过程在到解析再到返回的流程就跟大家分析完了,最后画个图给大家总结一下


image.png

分析先到这里,下一步编写自己的zxing扫描程序通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(二)

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

推荐阅读更多精彩内容

  • 二维码扫描最近两年简直是风靡移动互联网时代,尤其在国内发展神速。围绕条码扫码功能,首先说说通过本文你可以知道啥。一...
    55book阅读 4,116评论 0 1
  • 又是一个深夜,家人都休息了,房间里只有敲击键盘的声音,愈发显得安静。 电脑前的小强终于伸伸懒腰,停止了一天的写作。...
    静心漫时光阅读 436评论 7 10
  • 喜欢一个人 很久很久…… 从小学到大学,走过纯真,尝过青涩,步入成熟. 那个人就是 林俊杰. 那时候,每天都听CD...
    Janayer阅读 950评论 8 4
  • 天与水拼命挤出金色藤蔓,最终铺满人间。可是生命把这荣光私藏,于是黑夜降临。
    木言寺阅读 151评论 0 0
  • 你想对结营后的自己说什么? 首先,祝贺你克服重重困难完成任务,真的不是件容易的事,辛苦你了。然后,提醒你训练营的结...
    廖hf阅读 167评论 0 0