zxing二维码扫描的流程简析(Android版)

目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷。。。

下载下来后定位两个文件夹,core和android,core是一些核心的库,android是针对android的一些代码。

我们先看核心库,在package com.google.zxing中的一些生成二维码的类关系

接口Writer里面有两个encode的重载函数,不同的格式的二维码有各自的类实现了Writer接口,MultiformatWriter类比较特殊,根据代码的注释可见其其实是个工厂类,根据BarcodeFormat实例化不同的Writer,然后最终调用各自的Encode.encode()方法

 1 public final class MultiFormatWriter implements Writer {
 2 
 3   @Override
 4   public BitMatrix encode(String contents,
 5                           BarcodeFormat format,
 6                           int width,
 7                           int height) throws WriterException {
 8     return encode(contents, format, width, height, null);
 9   }
10 
11   @Override
12   public BitMatrix encode(String contents,
13                           BarcodeFormat format,
14                           int width, int height,
15                           Map<EncodeHintType,?> hints) throws WriterException {
16 
17     Writer writer;
18     switch (format) {
19       case EAN_8:
20         writer = new EAN8Writer();
21         break;
22       case EAN_13:
23         writer = new EAN13Writer();
24         break;
25       case UPC_A:
26         writer = new UPCAWriter();
27         break;
28       case QR_CODE:
29         writer = new QRCodeWriter();
30         break;
31       case CODE_39:
32         writer = new Code39Writer();
33         break;
34       case CODE_128:
35         writer = new Code128Writer();
36         break;
37       case ITF:
38         writer = new ITFWriter();
39         break;
40       case PDF_417:
41         writer = new PDF417Writer();
42         break;
43       case CODABAR:
44         writer = new CodaBarWriter();
45         break;
46       case DATA_MATRIX:
47         writer = new DataMatrixWriter();
48         break;
49       case AZTEC:
50         writer = new AztecWriter();
51         break;
52       default:
53         throw new IllegalArgumentException("No encoder available for format " + format);
54     }
55     return writer.encode(contents, format, width, height, hints);
56   }
57 
58 }

然后看解析二维码的类结构

关键就是这个MultiformatReader,里面聚合了多个reader,并且根据客户端设置的DecodeHintType值,确定添加reader以及添加reader的顺序,最后调用reader.decode方法

  1 public final class MultiFormatReader implements Reader {
  2 
  3   private Map<DecodeHintType,?> hints;
  4   private Reader[] readers;
  5 
  6   @Override
  7   public Result decode(BinaryBitmap image) throws NotFoundException {
  8     setHints(null);
  9     return decodeInternal(image);
 10   }
 11 
 12   @Override
 13   public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {
 14     setHints(hints);
 15     return decodeInternal(image);
 16   }
 17 
 18   public Result decodeWithState(BinaryBitmap image) throws NotFoundException {
 19     // Make sure to set up the default state so we don't crash
 20     if (readers == null) {
 21       setHints(null);
 22     }
 23     return decodeInternal(image);
 24   }
 25 
 26   public void setHints(Map<DecodeHintType,?> hints) {//根据设置的hint来设置reader
 27     this.hints = hints;
 28 
 29     boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
 30     @SuppressWarnings("unchecked")
 31     Collection<BarcodeFormat> formats =
 32         hints == null ? null : (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);
 33     Collection<Reader> readers = new ArrayList<Reader>();
 34     if (formats != null) {
 35       boolean addOneDReader =
 36           formats.contains(BarcodeFormat.UPC_A) ||
 37           formats.contains(BarcodeFormat.UPC_E) ||
 38           formats.contains(BarcodeFormat.EAN_13) ||
 39           formats.contains(BarcodeFormat.EAN_8) ||
 40           formats.contains(BarcodeFormat.CODABAR) ||
 41           formats.contains(BarcodeFormat.CODE_39) ||
 42           formats.contains(BarcodeFormat.CODE_93) ||
 43           formats.contains(BarcodeFormat.CODE_128) ||
 44           formats.contains(BarcodeFormat.ITF) ||
 45           formats.contains(BarcodeFormat.RSS_14) ||
 46           formats.contains(BarcodeFormat.RSS_EXPANDED);
 47       // Put 1D readers upfront in "normal" mode
 48       if (addOneDReader && !tryHarder) {
 49         readers.add(new MultiFormatOneDReader(hints));
 50       }
 51       if (formats.contains(BarcodeFormat.QR_CODE)) {
 52         readers.add(new QRCodeReader());
 53       }
 54       if (formats.contains(BarcodeFormat.DATA_MATRIX)) {
 55         readers.add(new DataMatrixReader());
 56       }
 57       if (formats.contains(BarcodeFormat.AZTEC)) {
 58         readers.add(new AztecReader());
 59       }
 60       if (formats.contains(BarcodeFormat.PDF_417)) {
 61          readers.add(new PDF417Reader());
 62       }
 63       if (formats.contains(BarcodeFormat.MAXICODE)) {
 64          readers.add(new MaxiCodeReader());
 65       }
 66       // At end in "try harder" mode
 67       if (addOneDReader && tryHarder) {
 68         readers.add(new MultiFormatOneDReader(hints));
 69       }
 70     }
 71     if (readers.isEmpty()) {
 72       if (!tryHarder) {
 73         readers.add(new MultiFormatOneDReader(hints));
 74       }
 75 
 76       readers.add(new QRCodeReader());
 77       readers.add(new DataMatrixReader());
 78       readers.add(new AztecReader());
 79       readers.add(new PDF417Reader());
 80       readers.add(new MaxiCodeReader());
 81 
 82       if (tryHarder) {
 83         readers.add(new MultiFormatOneDReader(hints));
 84       }
 85     }
 86     this.readers = readers.toArray(new Reader[readers.size()]);
 87   }
 88 
 89   @Override
 90   public void reset() {
 91     if (readers != null) {
 92       for (Reader reader : readers) {
 93         reader.reset();
 94       }
 95     }
 96   }
 97 
 98   private Result decodeInternal(BinaryBitmap image) throws NotFoundException {//最终都调用这个方法
 99     if (readers != null) {
100       for (Reader reader : readers) {
101         try {
102           return reader.decode(image, hints);
103         } catch (ReaderException re) {
104           // continue
105         }
106       }
107     }
108     throw NotFoundException.getNotFoundInstance();
109   }
110 
111 }

DecodeHintType的语法比较有意思,还在理解中

 1 public enum DecodeHintType {
 2 
 3   /**
 4    * Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
 5    */
 6  OTHER(Object.class),
 7 
 8   /**
 9    * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
10    * use {@link Boolean#TRUE}.
11    */
12   PURE_BARCODE(Void.class),
13 
14   /**
15    * Image is known to be of one of a few possible formats.
16    * Maps to a {@link List} of {@link BarcodeFormat}s.
17    */
18   POSSIBLE_FORMATS(List.class),
19 
20   /**
21    * Spend more time to try to find a barcode; optimize for accuracy, not speed.
22    * Doesn't matter what it maps to; use {@link Boolean#TRUE}.
23    */
24   TRY_HARDER(Void.class),
25 
26   /**
27    * Specifies what character encoding to use when decoding, where applicable (type String)
28    */
29   CHARACTER_SET(String.class),
30 
31   /**
32    * Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
33    */
34   ALLOWED_LENGTHS(int[].class),
35 
36   /**
37    * Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
38    * use {@link Boolean#TRUE}.
39    */
40   ASSUME_CODE_39_CHECK_DIGIT(Void.class),
41 
42   /**
43    * Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
44    * For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
45    * use {@link Boolean#TRUE}.
46    */
47   ASSUME_GS1(Void.class),
48 
49   /**
50    * The caller needs to be notified via callback when a possible {@link ResultPoint}
51    * is found. Maps to a {@link ResultPointCallback}.
52    */
53   NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class),
54 
55   // End of enumeration values.
56   ;
57 
58   /**
59    * Data type the hint is expecting.
60    * Among the possible values the {@link Void} stands out as being used for
61    * hints that do not expect a value to be supplied (flag hints). Such hints
62    * will possibly have their value ignored, or replaced by a
63    * {@link Boolean#TRUE}. Hint suppliers should probably use
64    * {@link Boolean#TRUE} as directed by the actual hint documentation.
65    */
66   private final Class<?> valueType;
67 
68   DecodeHintType(Class<?> valueType) {
69     this.valueType = valueType;
70   }
71   
72   public Class<?> getValueType() {
73     return valueType;
74   }
75 
76 }

然后我们看下android里面是如何调用的,入口是CaptureActivity,在com.google.zxing.client.android package中,以下描述一个通用的流程

CaptureAct中的onResume中的initCamera初始化CaptureActHandler,其构造函数中新起了一个DecodeThread去异步准备一个DecodeHandler,然后调用restartPreviewAndDecode方法,让DecodeHandler去处理R.id.decode的消息,当然这里需要处理一些线程同步问题,代码里用到了CountDownLatch来控制。DecodeHanlder处理R.id.decode消息后用传递R.id.decode_succeeded消息给CaptureActHanlder,最终再调用handleDecode传递给CaptureAct.

posted @ 2013-09-16 20:57  nickycookie  阅读(4710)  评论(6编辑  收藏  举报