Skip to content

Commit 829d0ea

Browse files
committed
feat: implement dedicated handler thread for IO operations
This prevents accidental simultaneous calls to the Android NFC methods, as well as performing better due to the lack of overhead from spawning new threads on each call.
1 parent 22f94c3 commit 829d0ea

File tree

1 file changed

+33
-19
lines changed

1 file changed

+33
-19
lines changed

android/src/main/kotlin/im/nfc/flutter_nfc_kit/FlutterNfcKitPlugin.kt

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.nfc.NfcAdapter
88
import android.nfc.NfcAdapter.*
99
import android.nfc.tech.*
1010
import android.os.Handler
11+
import android.os.HandlerThread
1112
import android.os.Looper
1213
import im.nfc.flutter_nfc_kit.ByteUtils.canonicalizeData
1314
import im.nfc.flutter_nfc_kit.ByteUtils.hexToBytes
@@ -30,7 +31,6 @@ import java.lang.ref.WeakReference
3031
import java.lang.reflect.InvocationTargetException
3132
import java.util.*
3233
import kotlin.concurrent.schedule
33-
import kotlin.concurrent.thread
3434

3535

3636
class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
@@ -43,6 +43,9 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
4343
private var ndefTechnology: Ndef? = null
4444
private var mifareInfo: MifareInfo? = null
4545

46+
private lateinit var nfcHandlerThread: HandlerThread
47+
private lateinit var nfcHandler: Handler
48+
4649
private fun TagTechnology.transceive(data: ByteArray, timeout: Int?): ByteArray {
4750
if (timeout != null) {
4851
try {
@@ -53,13 +56,27 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
5356
val transceiveMethod = this.javaClass.getMethod("transceive", ByteArray::class.java)
5457
return transceiveMethod.invoke(this, data) as ByteArray
5558
}
59+
60+
private fun runOnNfcThread(result: Result, fn: () -> Unit) {
61+
if (!nfcHandler.post(fn)) {
62+
result.error("500", "Failed to post job to NFC Handler thread.", null)
63+
}
64+
}
5665
}
5766

5867
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
68+
nfcHandlerThread = HandlerThread("NfcHandlerThread")
69+
nfcHandlerThread.start()
70+
nfcHandler = Handler(nfcHandlerThread.looper)
71+
5972
val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_nfc_kit")
6073
channel.setMethodCallHandler(this)
6174
}
6275

76+
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
77+
nfcHandlerThread.quitSafely()
78+
}
79+
6380
override fun onMethodCall(call: MethodCall, result: Result) {
6481
handleMethodCall(call, MethodResultWrapper(result))
6582
}
@@ -113,14 +130,14 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
113130
val timeout = call.argument<Int>("timeout")!!
114131
// technology and option bits are set in Dart code
115132
val technologies = call.argument<Int>("technologies")!!
116-
thread {
133+
runOnNfcThread(result) {
117134
pollTag(nfcAdapter, result, timeout, technologies)
118135
}
119136
}
120137

121138
"finish" -> {
122139
pollingTimeoutTask?.cancel()
123-
thread {
140+
runOnNfcThread(result) {
124141
try {
125142
val tagTech = tagTechnology
126143
if (tagTech != null && tagTech.isConnected) {
@@ -155,7 +172,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
155172
}
156173
val (sendingBytes, sendingHex) = canonicalizeData(data)
157174

158-
thread {
175+
runOnNfcThread(result) {
159176
try {
160177
switchTechnology(tagTech, ndefTechnology)
161178
val timeout = call.argument<Int>("timeout")
@@ -187,7 +204,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
187204
"readNDEF" -> {
188205
if (!ensureNDEF()) return
189206
val ndef = ndefTechnology!!
190-
thread {
207+
runOnNfcThread(result) {
191208
try {
192209
switchTechnology(ndef, tagTechnology)
193210
// read NDEF message
@@ -236,7 +253,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
236253
result.error("405", "Tag not writable", null)
237254
return
238255
}
239-
thread {
256+
runOnNfcThread(result) {
240257
try {
241258
switchTechnology(ndef, tagTechnology)
242259
// generate NDEF message
@@ -283,7 +300,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
283300
result.error("405", "Tag not writable", null)
284301
return
285302
}
286-
thread {
303+
runOnNfcThread(result) {
287304
try {
288305
switchTechnology(ndef, tagTechnology)
289306
if (ndef.makeReadOnly()) {
@@ -316,22 +333,22 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
316333
}
317334
val keyA = call.argument<Any>("keyA")
318335
val keyB = call.argument<Any>("keyB")
319-
thread {
336+
runOnNfcThread(result) {
320337
try {
321338
val tag = tagTech as MifareClassic
322339
switchTechnology(tagTech, ndefTechnology)
323340
// key A takes precedence if present
324-
val success = if (keyA != null) {
341+
if (keyA != null) {
325342
val (key, _) = canonicalizeData(keyA)
326-
tag.authenticateSectorWithKeyA(index, key)
343+
val authStatus = tag.authenticateSectorWithKeyA(index, key)
344+
result.success(authStatus)
327345
} else if (keyB != null) {
328346
val (key, _) = canonicalizeData(keyB)
329-
tag.authenticateSectorWithKeyB(index, key)
347+
val authStatus = tag.authenticateSectorWithKeyB(index, key)
348+
result.success(authStatus)
330349
} else {
331350
result.error("400", "No keys provided", null)
332-
return@thread
333351
}
334-
result.success(success)
335352
} catch (ex: IOException) {
336353
Log.e(TAG, "Authenticate block error", ex)
337354
result.error("500", "Authentication error", ex.localizedMessage)
@@ -351,7 +368,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
351368
result.error("400", "Invalid block/page index $index, should be in (0, $maxBlock)", null)
352369
return
353370
}
354-
thread {
371+
runOnNfcThread(result) {
355372
try {
356373
switchTechnology(tagTech, ndefTechnology)
357374
tagTech.readBlock(index, result)
@@ -374,7 +391,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
374391
result.error("400", "Invalid sector index $index, should be in (0, $maxSector)", null)
375392
return
376393
}
377-
thread {
394+
runOnNfcThread(result) {
378395
try {
379396
val tag = tagTech as MifareClassic
380397
switchTechnology(tagTech, ndefTechnology)
@@ -407,7 +424,7 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
407424
result.error("400", "Invalid data size ${bytes.size}, should be ${mifareInfo!!.blockSize}", null)
408425
return
409426
}
410-
thread {
427+
runOnNfcThread(result) {
411428
try {
412429
switchTechnology(tagTech, ndefTechnology)
413430
tagTech.writeBlock(index, bytes, result)
@@ -421,9 +438,6 @@ class FlutterNfcKitPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
421438
}
422439
}
423440

424-
425-
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}
426-
427441
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
428442
activity = WeakReference(binding.activity)
429443
}

0 commit comments

Comments
 (0)