android nfc中Ndef格式的读写
检测到标签后在Activity中的处理流程
1. 在onCreate()中获取NfcAdapter对象;
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
2.在onNewIntent()中获取Tag对象或者NdefMessage信息;
获取Tag对象:
Tag tag = intent.getParcelableExra(NfcAdapter.EXTRA_TAG);
获取NdefMessage信息:
Parcelable[] rawMsgs = getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
3.也可以通过Tag创建Ndef对象等,以实现标签的属性和I/O操作。
Ndef ndef = Ndef.get(tag);
NDEF格式标签的读取流程
1. 在onCreate()中获取NfcAdapter对象;
2.在onNewIntent()中判断是否为NDEF格式标签(ACTION_NDEF_DISCOVERED),若是则获取NdefMessage
信息;(需要强制转换成NdefMessage对象)
Parcelable[] rawMsgs = getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
3.对NdefMessage对象进行解析,获取相关的文本信息或Uri等。
NDEF格式标签的写入流程
1. 在onCreate()中获取NfcAdapter对象;
2.在onNewIntent()中获取Tag对象;
Tag tag = intent.getParcelableExra(NfcAdapter.EXTRA_TAG);
3.通过Tag创建Ndef对象;
Ndef ndef = Ndef.get(tag);
4.将文本等数据封装成NdefMessage;
5.判断是否为NDEF格式标签,
若是NDEF格式:
(1)允许进行标签操作:ndef.connect();
(2) 调用ndef.writeNdefMessage(NdefMessage)方法写入。
若非NDEF格式:
(1)NdefFromatable format = NdefFromatable.get();
(2)允许进行标签操作:format.connect();
(3)调用format.format(NdefMessage)方法写入。
NdefMessage信息结构
NdefRecord中的常用方法
1.可通过NdefRecord.getTnf()方法来获得TNF字段;
2.通过NdefRecord.getType()方法来获得RTD字段,当TNF为TNF_WELL_KNOWN时的RTD。
3.通过NdefRecord.getPayload()方法来获得实际读写的数据。
NDEF文本格式
NdefMessage中的paylaod就是实际的数据,其中NDEF文本格式为:
NDEF Uri格式
1、NdefMessage中的paylaod就是实际的数据,其中NDEF文本格式为:
2、前缀需要查表解析
例子程序:
ReadWriteTextMainActivity:
package mobile.android.read.write.text; import java.nio.charset.Charset; import java.util.Locale; import android.app.Activity; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.Ndef; import android.nfc.tech.NdefFormatable; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.widget.Toast; public class ReadWriteTextMainActivity extends Activity { private TextView mInputText; private String mText; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_read_write_text_main); mInputText = (TextView) findViewById(R.id.textview_input_text); } //单击“输入要写入文本”按钮执行的方法 public void onClick_InputText(View view) { Intent intent = new Intent(this, InputTextActivity.class); //显示输入文本的界面 startActivityForResult(intent, 1); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1 && resultCode == 1) { //获取要写入标签的文本 mText = data.getStringExtra("text"); //在主界面显示要写入标签的文本 mInputText.setText(mText); } } //当窗口的创建模式是singleTop或singleTask时调用,用于取代onCreate方法 //当NFC标签靠近手机,建立连接后调用 @Override public void onNewIntent(Intent intent) { //如果未设置要写入的文本,则读取标签上的文本数据 if (mText == null) { Intent myIntent = new Intent(this, ShowNFCTagContentActivity.class); //将intent传入另一个窗口,显示界面窗口 myIntent.putExtras(intent); //需要指定这个Action,传递Intent对象时,Action不会传递 myIntent.setAction(NfcAdapter.ACTION_NDEF_DISCOVERED); startActivity(myIntent); } //将指定的文本写入NFC标签 else { //获取Tag对象 Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //创建NdefMessage对象和NdefRecord对象 NdefMessage ndefMessage = new NdefMessage( new NdefRecord[] {createTextRecord(mText)}); //开始向标签写入文本 if (writeTag(ndefMessage, tag)) { //如果成功写入文本,将mtext设为null mText = null; //将主窗口显示的要写入的文本清空,文本只能写入一次 //如要继续写入,需要再次指定新的文本,否则只会读取标签中的文本 mInputText.setText(""); } } } //创建一个封装要写入的文本的NdefRecord对象 public NdefRecord createTextRecord(String text) { //生成语言编码的字节数组,中文编码 byte[] langBytes = Locale.CHINA.getLanguage().getBytes( Charset.forName("US-ASCII")); //将要写入的文本以UTF_8格式进行编码 Charset utfEncoding = Charset.forName("UTF-8"); //由于已经确定文本的格式编码为UTF_8,所以直接将payload的第1个字节的第7位设为0 byte[] textBytes = text.getBytes(utfEncoding); int utfBit = 0; //定义和初始化状态字节 char status = (char) (utfBit + langBytes.length); //创建存储payload的字节数组 byte[] data = new byte[1 + langBytes.length + textBytes.length]; //设置状态字节 data[0] = (byte) status; //设置语言编码 System.arraycopy(langBytes, 0, data, 1, langBytes.length); //设置实际要写入的文本 System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); //根据前面设置的payload创建NdefRecord对象 NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); return record; } //将NdefMessage对象写入标签,成功写入返回ture,否则返回false boolean writeTag(NdefMessage message, Tag tag) { int size = message.toByteArray().length; try { //获取Ndef对象 Ndef ndef = Ndef.get(tag); if (ndef != null) { //允许对标签进行IO操作 ndef.connect(); if (!ndef.isWritable()) { Toast.makeText(this, "NFC Tag是只读的!", Toast.LENGTH_LONG) .show(); return false; } if (ndef.getMaxSize() < size) { Toast.makeText(this, "NFC Tag的空间不足!", Toast.LENGTH_LONG) .show(); return false; } //向标签写入数据 ndef.writeNdefMessage(message); Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG).show(); return true; } else { //获取可以格式化和向标签写入数据NdefFormatable对象 NdefFormatable format = NdefFormatable.get(tag); //向非NDEF格式或未格式化的标签写入NDEF格式数据 if (format != null) { try { //允许对标签进行IO操作 format.connect(); format.format(message); Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG) .show(); return true; } catch (Exception e) { Toast.makeText(this, "写入NDEF格式数据失败!", Toast.LENGTH_LONG) .show(); return false; } } else { Toast.makeText(this, "NFC标签不支持NDEF格式!", Toast.LENGTH_LONG) .show(); return false; } } } catch (Exception e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); return false; } } }
InputTextActivity:
package mobile.android.read.write.text; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class InputTextActivity extends Activity { private EditText mTextTag; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_input_text); mTextTag = (EditText) findViewById(R.id.edittext_text_tag); } public void onClick_OK(View view) { Intent intent = new Intent(); intent.putExtra("text", mTextTag.getText().toString()); setResult(1, intent); finish(); } }
ShowNFCTagContentActivity:
package mobile.android.read.write.text; import mobile.android.read.write.text.library.TextRecord; import android.app.Activity; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.Ndef; import android.os.Bundle; import android.os.Parcelable; import android.widget.TextView; import android.widget.Toast; public class ShowNFCTagContentActivity extends Activity { private TextView mTagContent; private Tag mDetectedTag; private String mTagText; private void readAndShowData(Intent intent) { mDetectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); Ndef ndef = Ndef.get(mDetectedTag); mTagText = ndef.getType() + "\n最大数据容量:" + ndef.getMaxSize() + " bytes\n\n"; readNFCTag(); mTagContent.setText(mTagText); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_nfctag_content); mTagContent = (TextView) findViewById(R.id.textview_tag_content); //获取Tag对象 mDetectedTag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); //创建Ndef对象 Ndef ndef = Ndef.get(mDetectedTag); //获取标签的类型和最大容量 mTagText = ndef.getType() + "\n最大数据容量:" + ndef.getMaxSize() + " bytes\n\n"; //读取NFC标签的数据并解析 readNFCTag(); //将标签的相关信息显示在界面上 mTagContent.setText(mTagText); } private void readNFCTag() { //判断是否为ACTION_NDEF_DISCOVERED if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { //从标签读取数据(Parcelable对象) Parcelable[] rawMsgs = getIntent().getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); NdefMessage msgs[] = null; int contentSize = 0; if (rawMsgs != null) { msgs = new NdefMessage[rawMsgs.length]; //标签可能存储了多个NdefMessage对象,一般情况下只有一个NdefMessage对象 for (int i = 0; i < rawMsgs.length; i++) { //转换成NdefMessage对象 msgs[i] = (NdefMessage) rawMsgs[i]; //计算数据的总长度 contentSize += msgs[i].toByteArray().length; } } try { if (msgs != null) { //程序中只考虑了1个NdefRecord对象,若是通用软件应该考虑所有的NdefRecord对象 NdefRecord record = msgs[0].getRecords()[0]; //分析第1个NdefRecorder,并创建TextRecord对象 TextRecord textRecord = TextRecord.parse(msgs[0] .getRecords()[0]); //获取实际的数据占用的大小,并显示在窗口上 mTagText += textRecord.getText() + "\n\n纯文本\n" + contentSize + " bytes"; } } catch (Exception e) { mTagContent.setText(e.getMessage()); } } } }
TextRecord:
package mobile.android.read.write.text.library; import java.io.UnsupportedEncodingException; import java.util.Arrays; import android.nfc.NdefRecord; public class TextRecord { //存储解析出来的文本 private final String mText; //不允许直接创建TextRecord对象,所以将构造方法声明为private private TextRecord(String text) { mText = text; } //通过该方法可以获取解析出来的文本 public String getText() { return mText; } // 将纯文本内容从NdefRecord对象(payload)中解析出来 public static TextRecord parse(NdefRecord record) { //验证TNF是否为NdefRecord.TNF_WELL_KNOWN if (record.getTnf() != NdefRecord.TNF_WELL_KNOWN) return null; //验证可变长度类型是否为RTD_TEXT if (!Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) return null; try { //获取payload byte[] payload = record.getPayload(); //下面代码分析payload:状态字节+ISO语言编码(ASCLL)+文本数据(UTF_8/UTF_16) //其中payload[0]放置状态字节:如果bit7为0,文本数据以UTF_8格式编码,如果为1则以UTF_16编码 //bit6是保留位,默认为0 /* * payload[0] contains the "Status Byte Encodings" field, per the * NFC Forum "Text Record Type Definition" section 3.2.1. * * bit7 is the Text Encoding Field. * * if (Bit_7 == 0): The text is encoded in UTF-8 if (Bit_7 == 1): * The text is encoded in UTF16 * * Bit_6 is reserved for future use and must be set to zero. * * Bits 5 to 0 are the length of the IANA language code. */ String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16"; //处理bit5-0。bit5-0表示语言编码长度(字节数) int languageCodeLength = payload[0] & 0x3f; //获取语言编码(从payload的第2个字节读取languageCodeLength个字节作为语言编码) String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII"); //解析出实际的文本数据 String text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding); //创建一个TextRecord对象,并返回该对象 return new TextRecord(text); } catch (UnsupportedEncodingException e) { // should never happen unless we get a malformed tag. throw new IllegalArgumentException(e); } } }
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mobile.android.read.write.text" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.NFC" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".ReadWriteTextMainActivity" android:label="读写NFC标签的纯文本数据" android:launchMode="singleTask" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> <activity android:name=".ShowNFCTagContentActivity" android:label="显示NFC标签内容" android:launchMode="singleTask" /> <activity android:name=".InputTextActivity" android:label="向NFC标签写入文本" /> </application> </manifest>