美文网首页
通过谷歌官方例子手把手教你改造自己的Zxing扫描android

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

作者: 迷茫的Mr_Jing | 来源:发表于2018-07-11 18:03 被阅读0次

    在这个二维码满天飞的社会里,似乎每个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程序(二)

    相关文章

      网友评论

          本文标题:通过谷歌官方例子手把手教你改造自己的Zxing扫描android

          本文链接:https://www.haomeiwen.com/subject/hcbzuftx.html