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) { 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进行识别操作:
1 2 3 4 5
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); }
@Override public void surfaceCreated(SurfaceHolder holder) { if (holder == null) { Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!"); } if (!hasSurface) { hasSurface = true; initCamera(holder); } }
@Override public void surfaceDestroyed(SurfaceHolder holder) { hasSurface = false; }
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // do nothing }
使用 public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor)来处理返回到这层的识别的数据:
1 2 3 4 5 6 7 8
/** * A valid barcode has been found, so give an indication of success and show the results. * * @param rawResult The contents of the barcode. * @param scaleFactor amount by which thumbnail was scaled * @param barcode A greyscale bitmap of the camera data which was decoded. */ public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
switch (source) { case NATIVE_APP_INTENT: case PRODUCT_SEARCH_LINK: handleDecodeExternally(rawResult, resultHandler, barcode); break; case ZXING_LINK: if (scanFromWebPageManager == null || !scanFromWebPageManager.isScanFromWebPage()) { handleDecodeInternally(rawResult, resultHandler, barcode); } else { handleDecodeExternally(rawResult, resultHandler, barcode); } break; case NONE: SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); if (fromLiveScan && prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) { Toast.makeText(getApplicationContext(), getResources().getString(R.string.msg_bulk_mode_scanned) + " (" + rawResult.getText() + ')', Toast.LENGTH_SHORT).show(); maybeSetClipboard(resultHandler); // Wait a moment or else it will scan the same barcode continuously about 3 times restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS); } else { handleDecodeInternally(rawResult, resultHandler, barcode); } break;+ }
handleDecodeInternally主要是在UI上做显示:
1 2
// Put up our own UI for how to handle the decoded contents. private void handleDecodeInternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
而handleDecodeExternally则进一步往上一层传,如果外部有调用的话:
1 2
// Briefly show the contents of the barcode, then handle the result outside Barcode Scanner. private void handleDecodeExternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
this.activity = activity; handlerInitLatch = new CountDownLatch(1);
hints = new EnumMap<>(DecodeHintType.class); if (baseHints != null) { hints.putAll(baseHints); }
// The prefs can't change while the thread is running, so pick them up once here. if (decodeFormats == null || decodeFormats.isEmpty()) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); decodeFormats = EnumSet.noneOf(BarcodeFormat.class); if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D_PRODUCT, true)) { decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS); } if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D_INDUSTRIAL, true)) { decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS); } if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, true)) { decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS); } if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_DATA_MATRIX, true)) { decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS); } if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_AZTEC, false)) { decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS); } if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_PDF417, false)) { decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS); } } hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
QRCodeReader.java在“zxing-zxing-3.3.3\core\src\main\java\com\google\zxing\qrcode”文件夹下: 实现解码的函数为 public final Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints),在 public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException中被调用:
// If the code was mirrored: swap the bottom-left and the top-right points. if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) { ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points); }
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE); List<byte[]> byteSegments = decoderResult.getByteSegments(); if (byteSegments != null) { result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments); } String ecLevel = decoderResult.getECLevel(); if (ecLevel != null) { result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel); } if (decoderResult.hasStructuredAppend()) { result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE, decoderResult.getStructuredAppendSequenceNumber()); result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY, decoderResult.getStructuredAppendParity()); } return result; }
可以看到该函数先检测图片中的二维码,检测到之后再进行解码: 检测二维码:
1
DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(hints);
/** * This method detects a code in a "pure" image -- that is, pure monochrome image * which contains only an unrotated, unskewed, image of a code, with some white border * around it. This is a specialized method that works exceptionally fast in this special * case. * * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix) */ private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException
/** * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p> * * @param bits booleans representing white/black QR Code modules * @param hints decoding hints that should be used to influence decoding * @return text and bytes encoded within the QR Code * @throws FormatException if the QR Code cannot be decoded * @throws ChecksumException if error correction fails */ public DecoderResult decode(BitMatrix bits, Map<DecodeHintType,?> hints) throws FormatException, ChecksumException {
// Construct a parser and read version, error-correction level BitMatrixParser parser = new BitMatrixParser(bits); FormatException fe = null; ChecksumException ce = null; try { return decode(parser, hints); } catch (FormatException e) { fe = e; } catch (ChecksumException e) { ce = e; }
try {
// Revert the bit matrix parser.remask();
// Will be attempting a mirrored reading of the version and format info. parser.setMirror(true);
// Preemptively read the version. parser.readVersion();
// Preemptively read the format information. parser.readFormatInformation();
/* * Since we're here, this means we have successfully detected some kind * of version and format information when mirrored. This is a good sign, * that the QR code may be mirrored, and we should try once more with a * mirrored content. */ // Prepare for a mirrored reading. parser.mirror();
DecoderResult result = decode(parser, hints);
// Success! Notify the caller that the code was mirrored. result.setOther(new QRCodeDecoderMetaData(true));
return result;
} catch (FormatException | ChecksumException e) { // Throw the exception from the original reading if (fe != null) { throw fe; } throw ce; // If fe is null, this can't be } }
private DecoderResult decode(BitMatrixParser parser, Map<DecodeHintType,?> hints) throws FormatException, ChecksumException { Version version = parser.readVersion(); ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
// Read codewords byte[] codewords = parser.readCodewords(); // Separate into data blocks DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
// Count total number of data bytes int totalBytes = 0; for (DataBlock dataBlock : dataBlocks) { totalBytes += dataBlock.getNumDataCodewords(); } byte[] resultBytes = new byte[totalBytes]; int resultOffset = 0;
// Error-correct and copy data blocks together into a stream of bytes for (DataBlock dataBlock : dataBlocks) { byte[] codewordBytes = dataBlock.getCodewords(); int numDataCodewords = dataBlock.getNumDataCodewords(); correctErrors(codewordBytes, numDataCodewords); for (int i = 0; i < numDataCodewords; i++) { resultBytes[resultOffset++] = codewordBytes[i]; } }
// Decode the contents of that stream of bytes return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints); }