DEV Community

Cover image for Building an Android Barcode Reader: Use Barcode SDK for a successful outcome
Eric Parker πŸ₯‚
Eric Parker πŸ₯‚

Posted on

Building an Android Barcode Reader: Use Barcode SDK for a successful outcome

Different options exist when creating a mobile app that can scan barcodes on Android or iOS devices. You can choose between using open-source Software Development Kits (SDKs) like ZXing and ZBar, or you can opt for commercial barcode reading SDKs.

Commercial barcode reading SDKs deliver much better usability than their open-source counterparts. They offer a well-designed API and quality technical documentation to the developers. Also, ab easy to use SDK means less product launch time, eventually contributing to quicker revenue generation.

If you're an Android developer and have already installed the Android Play Services SDK version 26 or higher, there's a convenient way to create a barcode scanning app using the Android Vision API. Google has even provided a demo on GitHub that we can use as a reference to build our own barcode reader app for Android.

Now, let's break down the code and understand how it works to create our own version.

Android Barcode Demo with Google Vision API

To begin, you can download the sample code for Android Vision from the following link: https://github.com/googlesamples/android-vision

Once downloaded, open Android Studio and load the "barcode-reader" folder from the downloaded code.

Let's find the starting point in the code where we can work with the camera preview data. To do this, you can press Ctrl+Alt+Shift+N (or use the global search function) in Android Studio. Search for "onPreviewFrame" in the file named "CameraSource.java."

By locating and understanding this part of the code, you can begin exploring how to handle the camera preview and move forward with building our barcode scanning functionality.

private class CameraPreviewCallback implements Camera.PreviewCallback {
    @Override
    public void onPreviewFrame(byte\[\] data, Camera camera) {
        mFrameProcessor.setNextFrame(data, camera);
    }
}
Enter fullscreen mode Exit fullscreen mode

In the code, you'll come across an instance called "mFrameProcessor," which belongs to the FrameProcessingRunnable class. This instance is responsible for processing the preview data in a separate worker thread. The benefit of using a worker thread is that it prevents the barcode detection API from blocking the user interface (UI) thread, ensuring a smooth user experience.

Now, let's examine the "setNextFrame" method, which utilizes synchronization to lock the data. When a frame of preview data is received, it is cached and stored. Then, it notifies the worker thread to start processing and reading the barcode from the cached frame.

void setNextFrame(byte[] data, Camera camera) {
    synchronized (mLock) {
        if (mPendingFrameData != null) {
            camera.addCallbackBuffer(mPendingFrameData.array());
            mPendingFrameData = null;
        }
Enter fullscreen mode Exit fullscreen mode

In this part of the code, we have two important pieces of information that are being managed: the timestamp and the frame ID. These values are crucial for understanding the timing of the frames received and determining if any frames were dropped during the process.

     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());
                }
            }
        }
Enter fullscreen mode Exit fullscreen mode

Once we have the cached data, we use it to create an output frame. This frame is then passed to the "receiveFrame" method, which is an asynchronous interface.

To receive the results of the barcode detection, we need to associate the multi-processor instance, specifically the "BarcodeCaptureActivity.java" class, with the 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);
}
Enter fullscreen mode Exit fullscreen mode

Android Barcode Reader with Third-Party SDK

Google's demo provides us with a foundation to work with. We don't have to start building the barcode reader from scratch. Instead, we can use the demo as a starting point and make modifications, such as adding barcode format options. Additionally, we can replace the Google Vision API with the preview version of the Dynamsoft mobile barcode SDK for improved functionality.

Next, you can make a LinearLayout and add four Switch widgets into it:

<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>
Enter fullscreen mode Exit fullscreen mode

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);
    }
Enter fullscreen mode Exit fullscreen mode

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());
                }
            }
        }

Enter fullscreen mode Exit fullscreen mode

Scan barcode images:
Tap the screen to get the output:

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;
    }
Enter fullscreen mode Exit fullscreen mode

If you're interested in trying out the preview version of the Dynamsoft mobile barcode reader SDK for Android and iOS, you can reach out to support@dynamsoft.com for more information. Alternatively, if you prefer open source options, you can replace the Google Vision APIs with ZXing and ZBar, which are popular open source mobile barcode SDKs.

Source Code
https://github.com/yushulx/android-barcode-reader-demo

Top comments (0)