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 @Override12 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 // continue105 }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 for61 * hints that do not expect a value to be supplied (flag hints). Such hints62 * will possibly have their value ignored, or replaced by a63 * {@link Boolean#TRUE}. Hint suppliers should probably use64 * {@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.
更多相关文章
- android 工程里缺少 R.java 文件原因和解决方法
- Android软件广告屏蔽方法及代码
- Android异步处理常用方法
- Android 7.0系统启动流程分析
- Android设置TextView显示指定个数字符,超过部分显示...(省略号)的
- Android 环信官方Demo3.3.2详细配置方法