When building mobile barcode scanning apps for Android or iOS, developers could either choose open source SDKs – ZXing and ZBar, or some commercial mobile barcode SDKs. If you are an Android developer and have installed Android Play Services SDK level 26 or greater, it is convenient to create Android barcode reader apps with Android Vision API as well. Since Google has released a demo on GitHub, we can learn how to construct a simple Android barcode reader from the source code. Let’s anatomize the code and try to build our own version.
Android Barcode Demo with Google Vision API
Get the sample code of Android vision from https://github.com/googlesamples/android-vision, and load barcode-reader folder to Android Studio.
Press Ctrl+Alt+Shift+N to globally search for onPreviewFrame (CameraSource.java) which is the entry that we can get started with camera preview data:
private class CameraPreviewCallback implements Camera.PreviewCallback { @Override public void onPreviewFrame(byte[] data, Camera camera) { mFrameProcessor.setNextFrame(data, camera); } }
The mFrameProcessor is the instance of class FrameProcessingRunnable. According to the class name, we can speculate that it will process preview data in a worker thread. The advantage is that if the barcode detection API costs too much time, it will not block UI thread.
Take a look at setNextFrame which uses synchronization to lock the data. Once a frame of preview data is cached, it will notify the worker thread to do the reading work.
void setNextFrame(byte[] data, Camera camera) { synchronized (mLock) { if (mPendingFrameData != null) { camera.addCallbackBuffer(mPendingFrameData.array()); mPendingFrameData = null; } // Timestamp and frame ID are maintained here, which will give downstream code some // idea of the timing of frames received and when frames were dropped along the way. mPendingTimeMillis = SystemClock.elapsedRealtime() - mStartTimeMillis; mPendingFrameId++; mPendingFrameData = mBytesToByteBuffer.get(data); // Notify the processor thread if it is waiting on the next frame (see below). mLock.notifyAll(); } }
Press Ctrl+F12 to search for run:
public void run() { Frame outputFrame; ByteBuffer data; while (true) { synchronized (mLock) { if (mActive && (mPendingFrameData == null)) { try { mLock.wait(); } catch (InterruptedException e) { Log.d(TAG, "Frame processing loop terminated.", e); return; } } if (!mActive) { return; } outputFrame = new Frame.Builder() .setImageData(mPendingFrameData, mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.NV21) .setId(mPendingFrameId) .setTimestampMillis(mPendingTimeMillis) .setRotation(mRotation) .build(); data = mPendingFrameData; mPendingFrameData = null; } try { mDetector.receiveFrame(outputFrame); } catch (Throwable t) { Log.e(TAG, "Exception thrown from receiver.", t); } finally { mCamera.addCallbackBuffer(data.array()); } } }
Use the cached data to construct an output frame, and detect it with receiveFrame which is an asynchronous interface.
To receive the results, associate multi-processor instance (BarcodeCaptureActivity.java) to barcode detector:
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context).build(); BarcodeTrackerFactory barcodeFactory = new BarcodeTrackerFactory(mGraphicOverlay); barcodeDetector.setProcessor(new MultiProcessor.Builder<>(barcodeFactory).build());
Create a tracker (BarcodeTrackerFactory.java) for each barcode:
@Override public Tracker<Barcode> create(Barcode barcode) { BarcodeGraphic graphic = new BarcodeGraphic(mGraphicOverlay); return new BarcodeGraphicTracker(mGraphicOverlay, graphic); }
Update graphics (BarcodeGraphicTracker.java) as follows:
@Override public void onUpdate(Detector.Detections<Barcode> detectionResults, Barcode item) { mOverlay.add(mGraphic); mGraphic.updateItem(item); }
Android Barcode Reader with Third-Party SDK
Google’s demo is a skeleton for us. Based on it, we don’t need to write barcode reader from scratch. I will add some barcode format options and replace the Google Vision API with the preview version of Dynamsoft mobile barcode SDK:
Create a LinearLayout and add four Switch widgets into the layout:
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/auto_focus" android:layout_below="@+id/status_message" android:layout_centerVertical="true" android:orientation="vertical" > <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dbr_1d" android:id="@+id/dbr_1d" android:switchPadding="50dp" android:checked="true" android:layout_marginTop="180dp"/> <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dbr_qr" android:id="@+id/dbr_qr" android:switchPadding="50dp" android:checked="true" /> <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dbr_data_matrix" android:id="@+id/dbr_data_matrix" android:checked="true" /> <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dbr_pdf417" android:id="@+id/dbr_pdf" android:switchPadding="20dp" android:checked="true" /> </LinearLayout>
Create CameraListener (CameraSource.java) for asynchronously updating UI:
private CameraListener mCameraListener; public void setCameraListener(CameraListener listener) { mCameraListener = listener; } public interface CameraListener { public void onBarcodeResults(com.dynamsoft.barcode.ReadResult result); }
Replace com.google.android.gms.vision.Detector with com.dynamsoft.barcode.BarcodeReader:
public void run() { ByteBuffer data; while (true) { synchronized (mLock) { if (mActive && (mPendingFrameData == null)) { try { mLock.wait(); } catch (InterruptedException e) { Log.d(TAG, "Frame processing loop terminated.", e); return; } } if (!mActive) { return; } data = mPendingFrameData; mPendingFrameData = null; } try { byte[] buff = data.array(); com.dynamsoft.barcode.ReadResult dbrResult = mDetector.readSingle(buff, mPreviewSize.getWidth(), mPreviewSize.getHeight(), mBarcodeFormat); if (mCameraListener != null) { mCameraListener.onBarcodeResults(dbrResult); } } catch (Throwable t) { Log.e(TAG, "Exception thrown from receiver.", t); } finally { mCamera.addCallbackBuffer(data.array()); } } }
Scan barcode images:
Tap the screen to return the results:
private boolean onTap(float rawX, float rawY) { //TODO: use the tap position to select the barcode. BarcodeGraphic graphic = mGraphicOverlay.getFirstGraphic(); com.dynamsoft.barcode.Barcode barcode = null; if (graphic != null) { barcode = graphic.getBarcode(); if (barcode != null) { Intent data = new Intent(); data.putExtra(BarcodeObject, barcode.displayValue); setResult(1, data); finish(); } else { Log.d(TAG, "barcode data is null"); } } else { Log.d(TAG,"no barcode detected"); } return barcode != null; }
Dynamsoft mobile barcode reader SDK supports Android and iOS. If you want to try a preview version, please contact support@dynamsoft.com. You can also replace Google Vision APIs with ZXing and ZBar if you prefer open source mobile barcode SDKs.
Source Code
https://github.com/yushulx/android-barcode-reader-demo
The post How to Make Android Barcode Reader with Barcode SDK appeared first on Code Pool.