zxing 二维码扫描流程分析

网上有很多zxing 的扫描解析,但很少分析摄像头怎么和扫描过程联系,所以我自己看下zxing demo的源码,做了下面的总结,防止自己忘记。

项目结构,CaptureActivity是zxing的主activity,分析过的朋友知道,布局文件有个surfaceView,绘制摄像头的取景框。CaptureActivty实现了SurfaceHolder.Callback
。初始化摄像头和绘制工作就在这SurfaceHolder.Callback的两个方法里。我分析的时候用倒推的方式,因为看了一些分析的博文,虽然没有串联起来几个过程,但指明了几个方法的作用,这样能提高我分析的效率,这个之是我的习惯。
CaptureActivty中有个方法public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) ,这是分析扫描结果的方法。那这个方法由谁调用了,查找后是在CaptureActivityHandler的handlerMassage()方法中调用

 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);          
        }
        activity.handleDecode((Result) message.obj, barcode, scaleFactor);
        break;

这个是Handler,是处理 imessage.what为R.id.decode_success的事件中调用,那是哪里发送了这个message呢,经过查找有两处地方,一处是CaptureActivity的

 private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
    // Bitmap isn't used yet -- will be used soon
    if (handler == null) {
      savedResultToShow = result;
    } else {
      if (result != null) {
        savedResultToShow = result;
      }
      if (savedResultToShow != null) {
        Message message = Message.obtain(handler, R.id.decode_succeeded, savedResultToShow);
        handler.sendMessage(message);
      }
      savedResultToShow = null;
    }

这个方法有两个地方调用,在onActivityResult,因为没有涉及到其他activity,那么这里排除是由这里调用

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) {
        handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
      }
      decodeOrStoreSavedBitmap(null, null);
    } catch (IOException ioe) {

这是初始化摄像头的地方,传进decodeOrStoreSavedBitmap的两个参数是null,方法里不会执行到sendMessage的代码,这里也排除掉。剩下唯一的一处是在DecodeHandler
private void decode(byte[] data, int width, int height),下面是调用的地方。

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();
      }

那么这个方式是由谁调用的呢,是个private方法,所以还在本类中调用,果然

@Override
  public void handleMessage(Message message) {
    if (!running) {
      return;
    }
    switch (message.what) {
      case R.id.decode:
        decode((byte[]) message.obj, message.arg1, message.arg2);
        break;
      case R.id.quit:
        running = false;
        Looper.myLooper().quit();
        break;
    }
  }

得知DecodeHandler也是个Handler,那么在哪里发送这个R.id.decode的message呢,经过查找,没发现发送这个message的代码,不应该啊,再搜索一下,有个地方很可疑,是在CaptureActivityHandler的一个方法里

 private void restartPreviewAndDecode() {
    if (state == State.SUCCESS) {
      state = State.PREVIEW;
      cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
      activity.drawViewfinder();
    }
  }

进requestPreViewFrame看一下,这个方法在CameraManager中

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

看到给Camera注册了回调preViewCallback,previewCallback是类PreviewCallback对象,这个类实现了Camera.PreviewCallback接口,前面一句代码previewCallback.serHandler(…),把handler,message赋值给了这个callback的类变量。

void setHandler(Handler previewHandler, int previewMessage) {
    this.previewHandler = previewHandler;
    this.previewMessage = previewMessage;
  }

handler是decodeThread.getHandler()返回的,进入这个方法看一下就知道,其实返回的DecodeHandler ,是在decodeThread的run方法中赋值的。message 是R.id.decode,

setOneShotPreviewCallback的含义是当摄像头有可用的预览图像时会回调PreViewCallback的onPreviewFrame方法,下面是这个方法的实现

public void onPreviewFrame(byte[] data, Camera camera) {
    Point cameraResolution = configManager.getCameraResolution();
    Handler thePreviewHandler = previewHandler;
    if (cameraResolution != null && thePreviewHandler != null) {
      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");
    }
  }

thePreViewHandler的类型是DecodeHandler,preivewMessage的值是R.id.decode,终于找到了发送R.id.decode的地方。现在整理一下思路。
1首先在CaptureActivity中surfaceCreated调用了initCamera(),初始化摄像头
2.在CaptureActivity中的initCamera()中构造了CaptureActivityHandler对象
3.CaptureActivityHandler的构造方法里执行了线程DecodeThread(这个线程的作用就是构造了DecodeHander对象),并调用restartPreviewAndDecode()
4 restartPreviewAndDecode方法中调用CameraManager的方法requestPreviewFrame,在requestPreviewFrame方法中给摄像头注册了预览图像的回调。
5 当摄像头有了可用预览图像,调用回调接口的onPreviewFrame方法,在onPreviewFrame方法中执行了发送message为R.id.decode的事件
6 DecodeHanlder处理R.id.decode的事件,数据解析成功后,发送R.id.decode_succeeded的事件,处理该事件的是恰恰是CaptureActivityHandler。
7 CaptureActivityHandler没有处理具体的数据,只是将数据交给CaptureActivty的handleDecode()具体处理。

总结的只是个流程大概,里面还有很多细节没分析到,不过有了这个框架后,再分析细节,效率会大大提高。

,

猜你喜欢

转载自blog.csdn.net/hanshengjian/article/details/53957089