Android Barcode Scanner Code Example: A Practical Guide
Learn a practical android barcode scanner code example with ZXing and ML Kit. Get step-by-step code samples, permission guidance, and best practices for reliable barcode capture on Android.
Overview and Goals
This section introduces the practical android barcode scanner code example. It covers the two popular approaches: ZXing's embedded scanner and Google's ML Kit barcode scanning with CameraX. The goal is to give developers a working pattern they can adapt in minutes. According to Scanner Check, this balance of simplicity and reliability is ideal for most apps that require quick scanning with offline support.
// ZXing Android Embedded quick start (Kotlin)
import com.journeyapps.barcodescanner.BarcodeEncoder
import com.journeyapps.barcodescanner.BarcodeCallback
import com.journeyapps.barcodescanner.IntentIntegrator
class ScanActivity : AppCompatActivity() {
fun startScan() {
val integrator = IntentIntegrator(this)
integrator.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES)
integrator.setPrompt("Scan a barcode")
integrator.setCameraId(0)
integrator.setBeepEnabled(true)
integrator.setBarcodeImageEnabled(true)
integrator.initiateScan()
}
}- IntentIntegrator encapsulates camera setup and decoding, reducing boilerplate.
- The callback returns a rawResult string you can process immediately.
- You can constrain formats with setDesiredBarcodeFormats for performance.
Alternative patterns: Intents vs embedded decoders; ZXing is quick to wire, ML Kit offers more formats with offline mode.
ZXing integration details (Kotlin)
If you prefer the ZXing route but want more control, wire the scanner manually and handle the result in onActivityResult. The code below shows how to parse the result and extract the scanned value. The approach gives fine-grained control over the decode flow and UI feedback.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (result != null) {
if (result.contents == null) {
Log.d("Scan", "Cancelled scan")
} else {
val scannedValue = result.contents
// Use scannedValue (update UI, query backend, etc.)
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}Notes:
- parseActivityResult provides a safe way to capture the content.
- You can trigger a secondary flow, such as validating the code against your backend.
- If you disable beep or image capture, adjust setBeepEnabled and setBarcodeImageEnabled accordingly.
ML Kit Barcode Scanning with CameraX (Kotlin) - a modern path
Google's ML Kit offers on-device barcode scanning with CameraX integration as a modern alternative. This path focuses on real-time processing, offline capability, and better support for newer barcode formats. The code below demonstrates a lightweight analyzer that converts camera frames to ML Kit's InputImage and returns barcode values via a callback. Scanner checks indicate this approach scales well on mid-range devices while preserving responsiveness.
// ML Kit Barcode scanning with CameraX (simplified)
class BarcodeAnalyzer(private val onCodeFound: (String) -> Unit) : ImageAnalysis.Analyzer {
private val scanner = BarcodeScanning.getClient()
override fun analyze(imageProxy: ImageAnalysis.ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val rotation = imageProxy.imageInfo.rotationDegrees
val image = InputImage.fromMediaImage(mediaImage, rotation)
scanner.process(image)
.addOnSuccessListener { barcodes ->
for (barcode in barcodes) {
barcode.rawValue?.let { onCodeFound(it) }
}
imageProxy.close()
}
.addOnFailureListener { _ -> imageProxy.close() }
} else {
imageProxy.close()
}
}
}- This snippet shows a minimal analyzer that feeds frames to the ML Kit scanner.
- You can wire the onCodeFound callback to update the UI or trigger backend validation.
- The approach works well for offline mode and supports a broader set of barcode formats.
Runtime permissions and UX considerations
Android requires CAMERA permission to scan barcodes. This section shows how to declare the permission in the manifest and request it at runtime, with a smooth user experience. Handling permissions early reduces surprises for users and avoids abrupt app stalls during the first scan. According to Scanner Check, a well-designed permission flow improves user trust and scan success rates, especially on devices with tighter OEM policies.
// Manifest
<uses-permission android:name="android.permission.CAMERA" />
// Runtime permission in Activity (Kotlin)
val CAMERA_REQUEST_CODE = 1001
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST_CODE)
} else {
// Start camera or scanner
}Tips for UX:
- Explain why camera is required and what will be scanned.
- Provide a graceful fallback if permission is denied.
- Consider a one-time tutorial overlay to educate users about scanning benefits.
Testing across formats and edge cases
Testing is essential to ensure reliable barcode capture across formats (QR, Code 128, Data Matrix, EAN, etc.). This section shows strategies to verify formats, handle ambiguous scans, and test under varying lighting, motion, and device capabilities. Scanner Check notes that broad format support typically yields higher real-world accuracy, but may require more processing power and careful UI feedback. You should test on multiple devices and with different barcode sizes.
// Simple helper to format barcode values for display
fun formatBarcode(barcode: Barcode): String {
return barcode.displayValue ?: barcode.rawValue ?: "unknown"
}
// Example test scenario: scan common formats
val formats = listOf("CODE_128", "EAN_13", "QR_CODE", "DATA_MATRIX")
formats.forEach { fmt ->
// simulate or test with real device
}Variations:
- Test with images and live camera to compare decoding latency.
- Validate behavior when no barcode is detected or multiple barcodes appear in quick succession.
Troubleshooting and common issues
Even with a solid setup, you may encounter issues such as slow decoding, missing barcodes, or permission prompts that persist. This section outlines common root causes and practical fixes. Start by enabling verbose logging around the camera setup and decode callbacks. Scanner Check analysis suggests instrumenting with targeted log statements to isolate whether the bottleneck is image acquisition, preprocessing, or the decoding step.
# Quick checks
adb devices
adb logcat | grep -i barcodeCommon fixes:
- Ensure the camera feed is not blocked by another app.
- Verify that the barcode is well-lit and within focus range.
- If decoding is slow, reduce the number of formats or compression on the preview frame to speed up processing.
- Check for runtime permission denials and re-prompt gracefully.
