目前市面上二維碼的掃描彷佛用開源google的zxing比較多,接下去以2.2版本作一個簡析吧,勿噴。。。android
下載下來後定位兩個文件夾,core和android,core是一些核心的庫,android是針對android的一些代碼。git
咱們先看核心庫,在package com.google.zxing中的一些生成二維碼的類關係chrome
接口Writer裏面有兩個encode的重載函數,不一樣的格式的二維碼有各自的類實現了Writer接口,MultiformatWriter類比較特殊,根據代碼的註釋可見其實際上是個工廠類,根據BarcodeFormat實例化不一樣的Writer,而後最終調用各自的Encode.encode()方法app
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方法ide
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中,如下描述一個通用的流程this
CaptureAct中的onResume中的initCamera初始化CaptureActHandler,其構造函數中新起了一個DecodeThread去異步準備一個DecodeHandler,而後調用restartPreviewAndDecode方法,讓DecodeHandler去處理R.id.decode的消息,固然這裏須要處理一些線程同步問題,代碼裏用到了CountDownLatch來控制。DecodeHanlder處理R.id.decode消息後用傳遞R.id.decode_succeeded消息給CaptureActHanlder,最終再調用handleDecode傳遞給CaptureAct.google