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

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

Zbar
Zxing

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

Zxing demo工程导入

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

通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(一)_第1张图片
image.png

android 需要导入的只是
通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(一)_第2张图片
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'

    }

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


通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(一)_第3张图片
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 decodeFormats,
                         Map 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 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");
    }
  }

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


通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(一)_第4张图片
image.png

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

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