蛙仔老窦 發表於 2019-8-15 16:40:00

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

<p>在上一篇文章Android NFC开发之写入NDEF格式数据中我们介绍了如何往NFC标签中写入NDEF格式的数据</p>

<p>今天这篇文章介绍如何读取NFC标签中的NDEF格式数据</p>

<p>首先在AndroidManifest.xml文件中添加如下配置</p>

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

<p>创建一个NFC处理的基类</p>

<pre class="has"><code class="language-java">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);
      }
    }
}</code></pre>

<p>然后创建一个NFC读标的类ReadTagActivity,继承BaseNfcActivity</p>

<pre class="has"><code class="language-java">public class ReadTagActivity extends BaseNfcActivity {

    private TextView mContent;
    private String mTagText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_read_tag);

      mContent = findViewById(R.id.content);
    }
    @Override
    public void onNewIntent(Intent intent) {
      //1.获取Tag对象
      Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
      //2.获取Ndef的实例
      Ndef ndef = Ndef.get(detectedTag);
      mTagText = "type:" + ndef.getType();
      mTagText += "\nmaxsize:" + ndef.getMaxSize() + "bytes";
      readNfcTag(intent);
      mContent.setText(mTagText);
    }
    /**
   * 读取NFC标签文本数据
   */
    private void readNfcTag(Intent intent) {
      if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                  NfcAdapter.EXTRA_NDEF_MESSAGES);
            NdefMessage msgs[] = null;
            int contentSize = 0;
            if (rawMsgs != null) {
                msgs = new NdefMessage;
                for (int i = 0; i &lt; rawMsgs.length; i++) {
                  msgs = (NdefMessage) rawMsgs;
                  contentSize += msgs.toByteArray().length;
                }
            }
            try {
                if (msgs != null) {
                  NdefRecord record = msgs.getRecords();
                  String textRecord = parseTextRecord(record);
                  mTagText += "\ncontent:" + textRecord;
                  mTagText += "\ncontentSize:" + contentSize + " bytes";
                }
            } catch (Exception e) {
            }
      }
    }
    /**
   * 解析NDEF文本数据,从第三个字节开始,后面的文本数据
   * @param ndefRecord
   * @return
   */
    public static String parseTextRecord(NdefRecord ndefRecord) {
      /**
         * 判断数据是否为NDEF格式
         */
      //判断TNF
      if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
            return null;
      }
      //判断可变的长度的类型
      if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
            return null;
      }
      try {
            //获得字节数组,然后进行分析
            byte[] payload = ndefRecord.getPayload();
            //下面开始NDEF文本数据第一个字节,状态字节
            //判断文本是基于UTF-8还是UTF-16的,取第一个字节"位与"上16进制的80,16进制的80也就是最高位是1,
            //其他位都是0,所以进行"位与"运算后就会保留最高位
            String textEncoding = ((payload &amp; 0x80) == 0) ? "UTF-8" : "UTF-16";
            //3f最高两位是0,第六位是1,所以进行"位与"运算后获得第六位
            int languageCodeLength = payload &amp; 0x3f;
            //下面开始NDEF文本数据第二个字节,语言编码
            //获得语言编码
            String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
            //下面开始NDEF文本数据后面的字节,解析出文本
            String textRecord = new String(payload, languageCodeLength + 1,
                  payload.length - languageCodeLength - 1, textEncoding);
            return textRecord;
      } catch (Exception e) {
            throw new IllegalArgumentException();
      }
    }
}
</code></pre>

<p>布局文件很简单,就一个TextView显示读取的结果</p>

<pre class="has"><code class="language-html">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"&gt;
    &lt;TextView
      android:id="@+id/content"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"/&gt;
&lt;/LinearLayout&gt;</code></pre>

<p>最后记得把MainActivity的启动模式设置成singleTop</p>

<pre class="has"><code class="language-html">&lt;activity
    android:name=".ReadTagActivity"
    android:launchMode="singleTop"&gt;
    &lt;intent-filter&gt;
      &lt;action android:name="android.intent.action.MAIN" /&gt;
      &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
    &lt;/intent-filter&gt;
&lt;/activity&gt;</code></pre>

<h3>测试</h3>

<ol><li>首先准备一台支持NFC功能的手机,打开我们写好的应用</li>
        <li>准备一张NFC标签,靠近手机NFC读取区域(一般在背部)</li>
        <li>读取成功后会在页面显示标签的相关信息,如下所示</li>
</ol><p><img alt="" class="has" height="1200" src="https://img-blog.csdnimg.cn/20190815163912274.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21xZHhpYW94aWFv,size_16,color_FFFFFF,t_70" width="600"></p><br><br>
来源:https://www.cnblogs.com/yyhimmy/p/12583351.html
頁: [1]
查看完整版本: Android NFC开发之读取NDEF格式数据