Before you start
Keep the first test small and repeatable.
- An Android app with one workflow that needs a barcode result.
- The exact target model, Android build and approved device SDK identified.
- A decision about wedge, intent or direct SDK transport; do not mix them accidentally.
Step by step
Build it in 4 deliberate passes.
Each pass produces a checkable result. Keep device-specific calls in the adapter and keep the workflow portable.
- 01
Choose the transport for the workflow
Use wedge when a focused form needs a fast retrofit. Use an explicit intent or callback when the application owns the event. Use a direct device API only behind a small adapter so business screens do not depend on vendor types.
- 02
Define the event your application owns
Keep decoded text, raw bytes, symbology and received time together. Treat the transport payload as untrusted input and reject missing, oversized or undecodable data before it reaches inventory logic.
- 03
Open, listen, start and stop in one owner
Register the result path before starting a decode. Start only once, stop when the workflow ends, and close the device resource from every exit path including background, cancellation and errors.
- 04
Prove the unhappy paths
Test an empty payload, duplicate result, timeout, unsupported symbology, screen resume and a device with no scanner option. A green scan on one device is not a compatibility statement for every configuration.
Copyable pattern / Kotlin
Start with the boundary, then bind the device.
Original adapter pattern. Bind CapturePort to the approved device SDK in your private Android project.
data class CaptureEvent(
val value: String,
val symbology: String?,
val capturedAt: Instant,
)
interface CapturePort {
fun open()
fun start()
fun stop()
fun close()
fun setListener(listener: (CaptureEvent) -> Unit)
}
class ScanWorkflow(private val scanner: CapturePort) {
private var active = false
fun begin(onCapture: (CaptureEvent) -> Unit) {
if (active) return
scanner.open()
scanner.setListener(onCapture)
scanner.start()
active = true
}
fun end() {
if (!active) return
runCatching { scanner.stop() }
runCatching { scanner.close() }
active = false
}
}When the happy path breaks
Make recovery part of the first implementation.
Operators experience the failure state, not the API call. Translate device signals into a useful next action.
Return an input error and keep the workflow available for another scan.
Deduplicate by a short-lived event key; never silently double-receive inventory.
Stop the current operation, show a recoverable state and allow a controlled retry.
Stop or pause according to the workflow contract, then re-register safely on resume.
Before you ship
Use this checklist on the target configuration.
- 01
Record model, scanner option, Android build, firmware and SDK release.
- 02
Run cold start, repeated start/stop, background/resume and cancellation tests.
- 03
Use Code 128, a damaged label, an empty trigger and one unsupported symbology.
- 04
Confirm that no raw scan data or customer identifier is written to ordinary logs.
Further reading
Use platform guidance for the parts the device SDK does not own.
These references cover Android lifecycle, broadcast, testing and architecture patterns. Device-specific compatibility still needs a model-level validation record.