【问题标题】:Detect NFC tag through all the activities通过所有活动检测 NFC 标签
【发布时间】:2020-04-20 18:54:52
【问题描述】:

我正在开发基于 Android NFC 的应用程序,要求从任何活动中连续读取/写入数据到 SLIX-2(ICode) 标签。

截至目前,应用程序开始初始化 NFCManager,它完成了标签检测的大部分繁重工作,不断轮询存在检查、读取和写入数据。

BaseActivity 对 ANFCManager 进行初始化以及其他所需的工作,例如挂起的重启 Intent、检查 nfc 适配器、enableForegroundDispatch、...

private fun initField() {
  mNfcManager = ANfcManager(this)
}

private fun createPendingRestartIntent() {
        pendingIntent = PendingIntent.getActivity(this, 0, Intent(this, javaClass)
            .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0
        )
    }

override fun onResume() {
    super.onResume()
    try {
        if(mNfcManager.checkNfcPowerStatus()) // NfcAdapter enabled or not
            setReadyToHandleTag()
        else Log.w(TAG, "Nfc is not supported or disabled.") 

    } catch (e: AcmNfcManager.NfcNotEnabledException) {
        Log.e(TAG, "Nfc not enabled", e)
    }
}

private fun setReadyToHandleTag() {

    try {
        TECHLISTS = arrayOf(arrayOf(IsoDep::class.java.name), arrayOf(NfcV::class.java.name),
            arrayOf(NfcA::class.java.name), arrayOf(NfcB::class.java.name),
            arrayOf(NfcF::class.java.name),arrayOf(Ndef::class.java.name),
            arrayOf(NdefFormatable::class.java.name))

        val tagDetected = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
        tagDetected.addCategory(Intent.CATEGORY_DEFAULT)
        TAGFILTERS = arrayOf(tagDetected)
    } catch (e: Exception) {
        Log.e(TAG, "TECH or TAG filter no detected!!!" )
    }
    pendingIntent?.let { mNfcManager.enableForegroundDispatch(this, it, TAGFILTERS, TECHLISTS) }
}

 override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)

    nfcState = mNfcManager.filterIntent(intent)
    dispatchActionOnTag(nfcState)
}

// this abs function will provide the Tag state in the corresponding class
abstract fun dispatchActionOnTag(tag: Boolean)

每个 Activity 都有用于标签检测的 NfcListener,并将使用 ANfcManager API 进行读/写。也为了持续检查标签的存在,在 NFC 管理器中使用带有 looper 内部类的处理程序进行存在检查。

这里是ActivityA里面的函数,触发标签检测后的方法以及存在检查线程,

override fun dispatchActionOnTag(tag: Boolean) {
    mNfcStatus = tag
    if (nfcStateListener() != null) {
        nfcStateListener().updateNfcState(tag)
        mNfcManager.startTagCheck() // presence check handler every x sec
    }
} 

在标签检测和存在检查的每个活动中重复相同的功能(有点不干净但仍然有效),并基于对标签的读/写数据。

我的问题来了,

前提条件:

  • 我的应用程序(产品)中的标签位于固定位置(粘贴在硬件中),除非标签更改,否则通常不会取出。

  • 在大部分ActivityB或ActivityC的activity都会运行的情况下,有些情况可以取出Tag,这需要在这些activity中重复相同的回调代码。

必填: - 从 ActvityA-> ActivityB 切换时,标签检测流程未完成(onNewIntent)或标签未从接近中取出并再次点击。 我将如何向标签写入/读取数据?

ANFC 管理器,

class ANfcManager @Inject constructor(context: Context) {

    private val mContext = context
    private lateinit var nfcAdapter: NfcAdapter

    private lateinit var mTag: Tag
    private lateinit var iCodeTag: ICodeSlix2
    private lateinit var icode: ICode

init {

    val readPermission = ContextCompat.checkSelfPermission(
        mContext,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
    ) == PackageManager.PERMISSION_GRANTED

    if (!readPermission) {
        ActivityCompat.requestPermissions(
            mContext as Activity,
            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 113
        )
    }

    /**
     * initialize background thread for presence check every x seconds.
     */
    val thread = HandlerThread("PresenceCheckThread")
    thread.start()
    mHandler = PresenceHandler(thread.looper)
}

fun enableForegroundDispatch(
    activity: FragmentActivity, intent: PendingIntent,
    filters: Array<IntentFilter>?, techLists: Array<Array<String>>?
) {
    nfcAdapter.enableForegroundDispatch(activity, intent, filters, techLists)
}

fun disableForegroundDispatch(activity: Activity) {
    nfcAdapter.disableForegroundDispatch(activity)
}

fun filterIntent(intent: Intent): Boolean {
    val action = intent.action

    if (NfcAdapter.ACTION_TECH_DISCOVERED == action
        || NfcAdapter.ACTION_TAG_DISCOVERED == action
        || NfcAdapter.ACTION_NDEF_DISCOVERED == action
    ) {

        if (intent.hasExtra(NfcAdapter.EXTRA_TAG)) {
            mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)!!
            return if (discoverTag()) {
                Toast.makeText(mContext, "Tag detected.", Toast.LENGTH_SHORT).show()
                true
            } else {
                ignoreTag()
                false
            }
        }
    }
    return false
}

/**
 * discover the Tag family.
 */
fun discoverTag(): Boolean {
    icode = getTag(mTag)
    if (ICodeSlix2::class.java.isInstance(icode))
        iCodeTag = icode as ICodeSlix2
    return iCodeTag != null
}

fun checkNfcPowerStatus(): Boolean {
    return checkNfcPowerStatus(mContext)
}

/**
 * Check Nfc status
 */
private fun checkNfcPowerStatus(context: Context?): Boolean {
    nfcAdapter = NfcAdapter.getDefaultAdapter(context)
    var enabled = false
    if (nfcAdapter != null) {
        enabled = nfcAdapter.isEnabled
    }
    return enabled
}

fun writeUpdateBlocks() {
try {
    iCodeTag.connect()
.
. // proprietary code
.
}catch (e: IOException) {
    e.printStackTrace()
    Log.e(TAG, "IOException: ", e)
} catch (e: SmartCardException) {
    e.printStackTrace()
    Log.e(TAG, "SmartCardException: ", e)
} catch (e: IllegalArgumentException) {
    e.printStackTrace()
    Log.e(TAG, "IllegalArgumentException: ", e)
} catch (e: IllegalStateException) {
    e.printStackTrace()
    Log.e(TAG, "IllegalArgumentException: ", e)
} catch (e: IndexOutOfBoundsException) {
    e.printStackTrace()
    Log.e(TAG, "IndexOutOfBoundsException: ", e)
} finally {
    iCodeTag.close()
}

}

【问题讨论】:

    标签: android nfc


    【解决方案1】:

    必需: - 从 ActivityA-> ActivityB 切换时,标签检测 流程未完成(onNewIntent)或标签未从附近取出 并再次敲击。我将如何向标签写入/读取数据?

    所以 Tag 对象是 Parcelable Object ,只需将其从 ActivityA 传递给 ActivityB 即可,无需重新发现。

    例如类似的东西(对不起,Java 不是 Kotlin)
    活动A

    Intent intent = new Intent(getActivity(), ActivityB.class);
    intent.putExtra("TAG", mTag);
    startActivity(intent);
    
    

    ActivityB中的onCreate

    Intent intent = getIntent();
    mTag = intent.getParcelableExtra("TAG")
    // Start doing stuff with the Tag just like if you got it via discovery
    // ANfcManager might need a `setTag` method to set it without discovery.
    // or allow a Tag be be passed in the ANfcManager constructor
    
    

    并不是说我会使用enableForegroundDispatch 来读取标签,尤其是写入标签,因为我发现它太不可靠了,我会推荐enableReaderMode,但是您仍然可以在活动之间传递标签对象。

    【讨论】:

      【解决方案2】:

      将 Manager 类转换为 Singleton 很快,其余的都保持不变。

      基础活动,

      fun initField() {
         mNfcManager = ANfcManager.getInstance(this)
      }
      
      class ANfcManager private constructor(context: Context){
      
      companion object : SingletonHolder<ANfcManager, Context>(::ANfcManager) {
              val TAG = ANfcManager::class.java.simpleName
          }
      
         init{
           mContext = context
           .
           .
           .
         }
      }
      

      【讨论】:

        猜你喜欢
        • 2023-04-09
        • 2016-06-05
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-30
        相关资源
        最近更新 更多