首页 > 解决方案 > 使用 Android Studio 在标签上写入 NFC 数据

问题描述

我正在创建一个应用程序,在该应用程序中,我必须将来自文件的一系列值写入 NFC 卡,我一直在阅读,但我不知道从哪里真正开始,我有一些疑问。

首先,我知道理想的做法是创建一个处理 NFC 的类,尽管我认为这是可选的并且可以在同一个类中完成。问题是我看到的教程仅使用活动并使用 onNewIntent 方法。

在一个片段中我不能调用这个方法,所以这是我失败的第一步,我不知道这个方法是否必要,因为据我所知,这是启动应用程序,即使它像关闭一样关闭读者,如果我错了,请纠正我。如果您能指导我应该做什么,我将不胜感激,因为在阅读了这么多之后,我有点发疯了。

标签: androidkotlinnfc

解决方案


我要开始的第一个地方是考虑如何存储数据。

数据是为您的应用程序定制的,还是您想与其他应用程序共享?

应用程序会只写入一次数据还是会更新它(想要将数据附加到存储在卡上的现有数据中?

更新:根据您对数据类型的评论,您可能最好使用更高级别的 NDEF 格式来使用自定义 mime 类型存储您的数据。这是假设您选择的卡类型支持这一点。请注意,我给出的示例是使用低级命令读取/写入逐页读取和写入。

您要存储多少字节的数据(影响卡片技术)

您可能还想考虑要使用哪种 NFC 卡技术,可能是 NTAG 21x 系列卡之一的不错选择。

您要定位的最低 Android 版本是多少?

我不会使用 newIntent 方法,因为这对于写入数据非常不可靠,enableReaderMode如果您的目标是足够高的 Android 版本,我会使用。

https://developer.android.com/reference/android/nfc/NfcAdapter.html#enableReaderMode(android.app.Activity,%20android.nfc.NfcAdapter.ReaderCallback,%20int,%20android.os.Bundle)

您需要考虑的一些答案会影响示例的一些细节。

更新:基于评论即使您使用的是 Fragments,我仍然会将 NFC 处理机制放在 Activity 中。

这样做的原因是因为操作系统仍在处理标签发现,如果您不在每个片段中“声明”NFC 硬件,那么特别是对于 NDEF 数据格式,操作系统可能会在您的应用程序上显示一个屏幕,如果用户在错误的时间出示卡片,给用户带来不好的体验。

在我的多 Activity 应用程序中,我在每个 Activity 中“声明”NFC 硬件,即使它们中的很多都执行“发现标签时,什么也不做”,因为它们不是 NFC Activity。

因此,除非您想在每个 Fragment 中编写相同的代码,否则最好从您的一个 Activity 中调用 NFC 内容,然后onTagDiscovered执行类似(伪代码)的操作:-

更新:

if displaying the NFC user prompt Fragment.
get data to file.
write data to the card.
Notify user that it is done.
else
do nothing when other fragments are displayed.

或者您可以在应用程序打开的任何时候写卡(同样最好在活动中完成,而不是在任何片段中)

If card is presented no matter what fragment is being display
get data from the file
write data to the card
Notify user that it is done.

抱歉,我不能在 Kotlin 中做一个示例,但这是一个 Java 示例的准系统,从我的应用程序中提取(未经测试,因此可能存在复制和粘贴错误)


public class MainActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback{

private NfcAdapter mNfcAdapter;

@Override
    protected void onCreate(Bundle savedInstanceState) {
    // All normal onCreate Stuff

    // Listen to NFC setting changes
    this.registerReceiver(mReceiver, filter);
    }

    // Listen for NFC being turned on while in the App
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();

            if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) {
                final int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
                        NfcAdapter.STATE_OFF);
                switch (state) {
                    case NfcAdapter.STATE_OFF:
                    // Tell the user to turn NFC on if App requires it
                        break;
                    case NfcAdapter.STATE_TURNING_OFF:
                        break;
                    case NfcAdapter.STATE_ON:
                        enableNfc();
                        break;
                    case NfcAdapter.STATE_TURNING_ON:
                        break;
                }
            }
        }
    };

    @Override
    protected void onResume() {
        super.onResume();

        enableNfc();

    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mNfcAdapter!= null)
            mNfcAdapter.disableReaderMode(this);
    }




    private void enableNfc(){
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);

        if(mNfcAdapter!= null && mNfcAdapter.isEnabled()) {

            // Work around some buggy hardware that checks for cards too fast
            Bundle options = new Bundle();
            options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 1000);


            // Listen for all types of card when this App is in the foreground
            // Turn platform sounds off as they misdirect users when writing to the card
            // Turn of the platform decoding any NDEF data
            mNfcAdapter.enableReaderMode(this,
                    this,
                    NfcAdapter.FLAG_READER_NFC_A |
                            NfcAdapter.FLAG_READER_NFC_B |
                            NfcAdapter.FLAG_READER_NFC_F |
                            NfcAdapter.FLAG_READER_NFC_V |
                            NfcAdapter.FLAG_READER_NFC_BARCODE |
                            NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK |
                            NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                    options);
        } else {
            // Tell the user to turn NFC on if App requires it
        }
    }

    public void onTagDiscovered(Tag tag) {

        // This is run in a separate Thread to UI

        StringBuilder Uid = new StringBuilder();

        boolean successUid = getUID(tag, Uid);
        if (!successUid){
            // Not a successful read
            return;
        } else {
            // Feedback to user about successful read

            Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
            v.vibrate(500);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Update the UI / notify user  
                }
            });
            // Finish Task
            try {
                Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
                Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
                r.play();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public boolean getUID(Tag tag, StringBuilder Uid){
        NfcA mNfcA = NfcA.get(tag);

        if (mNfcA != null) {
            // The tag is NfcA capable
            try {
                mNfcA.connect();
                // Do a Read operation at page 0 an 1
                byte[] result = mNfcA.transceive(new byte[] {
                        (byte)0x3A,  // FAST_READ
                        (byte)(0 & 0x0ff),
                        (byte)(1 & 0x0ff),
                });

                if (result == null) {
                    // either communication to the tag was lost or a NACK was received
                    // Log and return
                    return false;
                } else if ((result.length == 1) && ((result[0] & 0x00A) != 0x00A)) {
                    // NACK response according to Digital Protocol/T2TOP
                    // Log and return
                    return false;
                } else {
                    // success: response contains ACK or actual data
                    for (int i = 0; i < result.length; i++) {
                        // byte 4 is a check byte
                        if (i == 3) continue;
                        Uid.append(String.format("%02X ", result[i]));
                    }

                    // Close and return
                    try {
                        mNfcA.close();
                    } catch (IOException e) {
                    }
                    return true;
                }

            } catch (TagLostException e) {
                // Log and return
                return false;
            } catch (IOException e){
                // Log and return
                return false;
            } finally {
                try {
                    mNfcA.close();
                } catch (IOException e) {
                }
            }
        } else {
            // Log error
            return false;
        }
    }

}


推荐阅读