Android NFC开发之读取NDEF格式Uri数据

首先在AndroidManifest.xml文件中添加如下配置

<!-- SDK版本至少为14 -->
<uses-sdk android:minSdkVersion="14"/>
<!-- 添加NFC权限 -->
<uses-permission android:name="android.permission.NFC" />
<!-- 要求当前设备必须要有NFC芯片 -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />

创建一个NFC处理的基类

public class BaseNfcActivity extends AppCompatActivity {
    private NfcAdapter mNfcAdapter;
    private PendingIntent mPendingIntent;

    @Override
    protected void onStart() {
        super.onStart();
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        // 用于感应到NFC时启动该Activity
        // 这里建议将处理NFC的子类的launchMode设置成singleTop模式,这样感应到标签时就会回调onNewIntent,而不会重复打开页面
        mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
    }
    /**
     * 获得焦点,按钮可以点击
     */
    @Override
    public void onResume() {
        super.onResume();
        // 设置当该页面处于前台时,NFC标签会直接交给该页面处理
        if (mNfcAdapter != null) {
            mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
        }
    }
    /**
     * 暂停Activity,界面获取焦点,按钮可以点击
     */
    @Override
    public void onPause() {
        super.onPause();
        // 当页面不可见时,NFC标签不交给当前页面处理
        if (mNfcAdapter != null) {
            mNfcAdapter.disableForegroundDispatch(this);
        }
    }
}

再创建一个Uri前缀查询解析的类

public class UriPrefixMap {
    public static final Map<Byte, String> URI_PREFIX_MAP = new HashMap<Byte, String>();
    static {
        URI_PREFIX_MAP.put((byte) 0x00, "");
        URI_PREFIX_MAP.put((byte) 0x01, "http://www.");
        URI_PREFIX_MAP.put((byte) 0x02, "https://www.");
        URI_PREFIX_MAP.put((byte) 0x03, "http://");
        URI_PREFIX_MAP.put((byte) 0x04, "https://");
        URI_PREFIX_MAP.put((byte) 0x05, "tel:");
        URI_PREFIX_MAP.put((byte) 0x06, "mailto:");
        URI_PREFIX_MAP.put((byte) 0x07, "ftp://anonymous:anonymous@");
        URI_PREFIX_MAP.put((byte) 0x08, "ftp://ftp.");
        URI_PREFIX_MAP.put((byte) 0x09, "ftps://");
        URI_PREFIX_MAP.put((byte) 0x0A, "sftp://");
        URI_PREFIX_MAP.put((byte) 0x0B, "smb://");
        URI_PREFIX_MAP.put((byte) 0x0C, "nfs://");
        URI_PREFIX_MAP.put((byte) 0x0D, "ftp://");
        URI_PREFIX_MAP.put((byte) 0x0E, "dav://");
        URI_PREFIX_MAP.put((byte) 0x0F, "news:");
        URI_PREFIX_MAP.put((byte) 0x10, "telnet://");
        URI_PREFIX_MAP.put((byte) 0x11, "imap:");
        URI_PREFIX_MAP.put((byte) 0x12, "rtsp://");
        URI_PREFIX_MAP.put((byte) 0x13, "urn:");
        URI_PREFIX_MAP.put((byte) 0x14, "pop:");
        URI_PREFIX_MAP.put((byte) 0x15, "sip:");
        URI_PREFIX_MAP.put((byte) 0x16, "sips:");
        URI_PREFIX_MAP.put((byte) 0x17, "tftp:");
        URI_PREFIX_MAP.put((byte) 0x18, "btspp://");
        URI_PREFIX_MAP.put((byte) 0x19, "btl2cap://");
        URI_PREFIX_MAP.put((byte) 0x1A, "btgoep://");
        URI_PREFIX_MAP.put((byte) 0x1B, "tcpobex://");
        URI_PREFIX_MAP.put((byte) 0x1C, "irdaobex://");
        URI_PREFIX_MAP.put((byte) 0x1D, "file://");
        URI_PREFIX_MAP.put((byte) 0x1E, "urn:epc:id:");
        URI_PREFIX_MAP.put((byte) 0x1F, "urn:epc:tag:");
        URI_PREFIX_MAP.put((byte) 0x20, "urn:epc:pat:");
        URI_PREFIX_MAP.put((byte) 0x21, "urn:epc:raw:");
        URI_PREFIX_MAP.put((byte) 0x22, "urn:epc:");
        URI_PREFIX_MAP.put((byte) 0x23, "urn:nfc:");
    }
}

然后创建一个NFC读标的类ReadTagUriActivity,继承BaseNfcActivity

public class ReadTagUriActivity extends BaseNfcActivity {

    private TextView mContent;
    private String content;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_read_tag_uri);
        mContent = findViewById(R.id.content);
    }

    // 感应到NFC标签会回调该方法
    @Override
    public void onNewIntent(Intent intent) {
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        Ndef ndef = Ndef.get(tag);
        content = "type:" + ndef.getType() + "\nmax size:" + ndef.getMaxSize() + "bytes";
        readNfcTag(intent);
        mContent.setText(content);
    }

    private void readNfcTag(Intent intent) {
        if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Toast.makeText(this, "非NDEF格式标签", Toast.LENGTH_SHORT).show();
            return;
        }
        Parcelable[] extra = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (extra == null) {
            return;
        }
        if (extra.length == 0) {
            return;
        }

        NdefMessage message = (NdefMessage) extra[0];
        int contentSize = message.toByteArray().length;
        NdefRecord record = message.getRecords()[0];
        Uri uri = parseUri(record);
        content += "\ncontent:" + uri.toString();
        content += "\ncontentSize:" + contentSize + " bytes";
    }

    public static Uri parseUri(NdefRecord record) {
        short tnf = record.getTnf();
        if (tnf == NdefRecord.TNF_WELL_KNOWN) {
            return parseWellKnownUri(record);
        } else if (tnf == NdefRecord.TNF_ABSOLUTE_URI) {
            return parseAbsoluteUri(record);
        }
        return null;
    }

    /**
     * 未知类型的uri,直接把byte[]转成字符串
     * @param record
     * @return
     */
    private static Uri parseAbsoluteUri(NdefRecord record) {
        byte[] payload = record.getPayload();
        Uri uri = Uri.parse(new String(payload, Charset.forName("UTF-8")));
        return uri;
    }

    /**
     * 解析已知类型的uri
     * @param record
     * @return
     */
    private static Uri parseWellKnownUri(NdefRecord record) {
        if (!Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
            return null;
        }
        byte[] payload = record.getPayload();
        // payload第一个字节为Uri识别码,根据Uri识别码查询出对应的Uri前缀
        String prefix = UriPrefixMap.URI_PREFIX_MAP.get(payload[0]);
        // 将Uri前缀转成byte数组
        byte[] prefixBytes = prefix.getBytes(Charset.forName("UTF-8"));
        // 创建一个新的byte数组,重新组合Uri前缀和Uri内容
        byte[] fullUri = new byte[prefixBytes.length + payload.length - 1];
        // 先组装Uri前缀
        System.arraycopy(prefixBytes, 0, fullUri, 0, prefixBytes.length);
        // 再组装Uri内容
        System.arraycopy(payload, 1, fullUri, prefixBytes.length, payload.length - 1);
        Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8")));
        return uri;
    }
}

布局文件很简单,就一个TextView显示读取结果

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

最后记得把ReadTagUriActivity的启动模式设置成singleTop

<activity
    android:name=".ReadTagUriActivity"
    android:launchMode="singleTop">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

测试

  1. 首先准备一台支持NFC功能的手机,打开我们写好的应用
  2. 准备一张写好Uri数据的NFC标签,靠近手机NFC读取区域(一般在背部)
  3. 读取成功后会在应用中显示读取结果

 

 

posted @ 2019-09-04 10:30  野猿新一  阅读(28)  评论(0编辑  收藏  举报