ASQUARETE

Zebra RFD40 Android Integration with RFID API3

Zebra RFD40 · Android · Updated Jul 18, 2026 · Device SDK

Zebra's own Hello RFID tutorial states outright that the demo is for tutorial purposes only and not for production use. Production RFD40 integration means handling Bluetooth reconnection explicitly, since the sled disconnects from its paired handheld far more often than the tutorial's happy-path flow accounts for.

The Zebra RFD40 is a sled-style UHF RFID reader that pairs with a host Android handheld over Bluetooth, rather than integrating a UHF module directly into a single device. That architecture — two devices, one radio link between them — is exactly where Zebra's own tutorial code falls short of production readiness, and Zebra says so directly.

Device photo — Zebra RFD40 sled-style UHF RFID reader paired with Android handheld
Zebra RFD40 — Bluetooth sled reader, pairs with a host Android device

What the vendor docs don't tell you

Zebra's "Hello RFID" tutorial is the standard starting point for RFD40 integration, and it includes a disclaimer that's easy to skim past: the sample is explicitly scoped to tutorial use, not production.

What the docs don't tell you

Zebra's own "Hello RFID" tutorial states directly that the demo "is intended for tutorial purposes only and should not be used in production environments." The gap isn't cosmetic — the tutorial's connection flow assumes the Bluetooth link between the RFD40 sled and the host handheld stays up for the duration of the demo, which is a reasonable assumption for a five-minute walkthrough and a false one for an eight-hour shift. Production code needs an explicit reconnection state machine that the tutorial never builds.

Setup

RFID API3 ships as a Zebra-distributed .jar/.aar through their developer portal (Zebra requires a developer account to access the SDK downloads).

dependencies {
    implementation files('libs/RFIDAPI3.jar')
    implementation files('libs/SrfidLibrary.aar')
}

Declare Bluetooth permissions appropriate to the target API level — API 31+ requires the runtime BLUETOOTH_CONNECT permission in addition to the manifest declaration.

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

Discovering and connecting to the sled

RFID API3 discovers available readers through the Readers class, then establishes a connection via RFIDReader.

class RfdConnectionManager(private val context: Context) {
    private var reader: RFIDReader? = null
 
    fun connect(onConnected: (RFIDReader) -> Unit, onError: (String) -> Unit) {
        val readers = Readers(context, ENUM_TRANSPORT.BLUETOOTH)
        val available = readers.GetAvailableRFIDReaderList()
 
        val target = available.firstOrNull() ?: run {
            onError("No RFD40 sled found in range")
            return
        }
 
        reader = target.rfidReader
        try {
            reader?.connect()
            reader?.Events?.setInvTagReadEvent(true)
            onConnected(reader!!)
        } catch (e: InvalidUsageException) {
            onError("Connect failed: ${e.message}")
        } catch (e: OperationFailureException) {
            onError("Operation failure: ${e.vendorMessage}")
        }
    }
}

Reconnection: the part the tutorial skips

Register a connection event listener and treat disconnection as a routine, expected state — not an exception path that only gets a log line.

reader?.Events?.addEventsListener(object : RfidEventsListener {
    override fun eventStatusNotify(statusEvents: RfidStatusEvents?) {
        when (statusEvents?.StatusEventData?.statusEventType) {
            STATUS_EVENT_TYPE.DISCONNECTION_EVENT -> {
                scope.launch { attemptReconnect(maxAttempts = 5) }
            }
            STATUS_EVENT_TYPE.CONNECTION_EVENT -> {
                onReconnected()
            }
            else -> Unit
        }
    }
 
    override fun eventReadNotify(e: RfidReadEvents?) { /* tag read handling */ }
})
 
private suspend fun attemptReconnect(maxAttempts: Int) {
    var attempt = 0
    while (attempt < maxAttempts) {
        delay(1000L * (attempt + 1)) // linear backoff
        try {
            reader?.connect()
            return
        } catch (e: Exception) {
            attempt++
        }
    }
    onReconnectExhausted()
}

Surface a visible UI state for "reconnecting" versus "disconnected, action needed" — an operator mid-shift needs to know whether to wait or physically re-pair the sled.

Reading tags

Once connected, inventory reads arrive through the same event listener rather than a polling loop, which is the main structural difference from UART-based handhelds like the Chainway line.

fun startInventory() {
    reader?.Actions?.Inventory?.perform()
}
 
fun stopInventory() {
    reader?.Actions?.Inventory?.stop()
}
 
// in eventReadNotify:
override fun eventReadNotify(e: RfidReadEvents?) {
    val tagData = reader?.Actions?.getReadTags(100) ?: return
    tagData.forEach { tag ->
        onTagRead(tag.tagID, tag.peakRSSI)
    }
}

Production considerations

  • Build the reconnection state machine before anything else. It's the single biggest gap between the tutorial and a shift-length deployment.
  • Release the reader connection explicitly (reader?.disconnect() / reader?.Dispose()) in onPause — a sled left connected to a backgrounded app can block a different app on the same handheld from acquiring it.
  • Handle OperationFailureException and InvalidUsageException distinctly rather than catching a generic Exception — API3 uses these to distinguish transient radio-level failures from usage errors that indicate a code bug.
  • Log disconnect frequency in the field. An RFD40 that disconnects far more than expected on a specific handheld model usually indicates a Bluetooth stack or power-management conflict specific to that host device, not the sled itself.

Common errors

ErrorCauseFix
Sled disconnects repeatedly during a shiftNo reconnection handling; tutorial code assumes a stable linkImplement a reconnection state machine with backoff, keyed off DISCONNECTION_EVENT
OperationFailureException on connectSled already claimed by another app or processEnsure only one app instance holds the connection; explicitly disconnect on app backgrounding
Tag reads stop after a backgrounded app resumesConnection silently dropped while backgrounded, not detected until next read attemptCheck connection state in onResume and reconnect proactively rather than waiting for a read failure
App crashes with InvalidUsageExceptionSDK method called before connect() completed successfullyGate all reader actions behind a confirmed CONNECTION_EVENT, not just a call to connect()

FAQ

Is Zebra's Hello RFID tutorial safe to ship as-is?

No. Zebra's own documentation states it is intended for tutorial purposes only and should not be used in production environments. It skips reconnection handling, error recovery, and the lifecycle management a real deployment needs.

What SDK should RFD40 integrations use?

RFID API3 for Android, using the Readers and RFIDReader classes. It's the current SDK generation Zebra supports for the RFD40 and related sled-style UHF readers.

Why does the RFD40 disconnect during a scan session?

The RFD40 pairs over Bluetooth to a host handheld, and Bluetooth connections degrade under RF interference, distance, or the host device's power management aggressively suspending idle BT connections. Reconnection needs to be handled as a routine event, not an edge case.

Does the RFD40 talk over a different protocol than Zebra's other readers?

It uses an ASCII-based protocol over its Bluetooth transport, wrapped by the RFID API3 SDK. Integrators don't typically parse the raw protocol directly — the SDK's event callbacks abstract it — but the disconnect-prone Bluetooth link underneath is the operationally relevant detail.

Asquarete · Published Jul 18, 2026 · Updated Jul 18, 2026

Related guides

Stuck on this device?

Feasibility read in 24 hours.

Get a feasibility read