检测到标签后在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信息结构
Supported TNFs and their mappings:
TNF_ABSOLUTE_URI |
URI based on the type field. |
TNF_EMPTY |
Falls back toACTION_TECH_DISCOVERED . |
TNF_EXTERNAL_TYPE |
URI based on the URN in the type field. The URN is encoded into the NDEF type field in a shortened form:<domain_name>:<service_name> . Android maps this to a URI in the form:vnd.android.nfc://ext/<domain_name>:<service_name> . |
TNF_MIME_MEDIA |
MIME type based on the type field. |
TNF_UNCHANGED |
Invalid in the first record, so falls back toACTION_TECH_DISCOVERED . |
TNF_UNKNOWN |
Falls back toACTION_TECH_DISCOVERED . |
TNF_WELL_KNOWN |
MIME type or URI depending on the Record Type Definition (RTD), which you set in the type field. SeeTable 2.for more information on available RTDs and their mappings. |
RTD_ALTERNATIVE_CARRIER |
Falls back toACTION_TECH_DISCOVERED . |
RTD_HANDOVER_CARRIER |
Falls back toACTION_TECH_DISCOVERED . |
RTD_HANDOVER_REQUEST |
Falls back toACTION_TECH_DISCOVERED . |
RTD_HANDOVER_SELECT |
Falls back toACTION_TECH_DISCOVERED . |
RTD_SMART_POSTER |
URI based on parsing the payload. |
RTD_TEXT |
MIME type oftext/plain . |
RTD_URI |
URI based on payload. |
说明:读取TNF的类型后(可以是上面第一张表中的类型),如果是TNF_WELL_KNOWN时,就可以获取RTD(对应格式良好的
{TNF_WELL_KNOWN}的标签的类型,可以是第二张表中的类型)
NdefRecord中的常用方法
1.可通过NdefRecord.getTnf()方法来获得TNF字段;
对应上图中Header中的Length
2.通过NdefRecord.getType()方法来获得RTD字段,当TNF为TNF_WELL_KNOWN时的RTD。
对应上图中Header中的Type
3.通过NdefRecord.getPayload()方法来获得实际读写的数据。
Header中的Identifier对应每个Record唯一的Id对应上图中的Payload
NDEF文本格式
NdefMessage中的paylaod就是实际的数据,其中NDEF文本格式为:
NDEF Uri格式
1、NdefMessage中的paylaod就是实际的数据,其中NDEF文本格式为:
2、前缀需要查表解析
Android应用程序记录Android Application Records(AAR)
1、在Android4.0中引入应用程序记录(AAR),当扫描到写入AAR的NFC标签时,启动相应的应用程序。
2、AAR有嵌入到NdefRecord内部的应用程序包名。Android会针对AAR来搜索整个NdefMessage,如果找到一个AAR,就会基于AAR内部的包名来启动应用程序。
3、NFC标签调度系统对包含AAR标签的调度:
1.若跟Intent匹配的Activity也跟AAR匹配,则启动该Activity;
2.若跟Intent匹配,而跟AAR不匹配,则启动AAR指定的应用程序;
3.如果没有跟AAR对应的应用程序,则启动各种市场来下载对应基于AAR的应用程序。
Android应用程序记录创建方法
1、调用NdefRecord类的creatApplicationRecord()方法来创建应用程序记录。
2、将所创建的AAR嵌入到NdefMessage中。
NdefMessage msg = new NdefMessage(new Ndefrecord[]{…,NdefRecord. creatApplicationRecord(“com.example.android.beam”)})
3、除非AAR是你NdefMessage中的唯一记录,否则不要将AAR嵌入到NdefMessage的第一条记录。
NDEF for Text 读写,例子程序:
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); } } }AndroidManifest.xml:
<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>
NDEF for URL 读写,例子程序:
ReadWriteUriMainActivity:
package mobile.android.read.write.uri; import mobile.android.read.write.uri.library.UriRecord; 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 ReadWriteUriMainActivity extends Activity { private TextView mSelectUri; private String mUri; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_read_write_uri_main); mSelectUri = (TextView) findViewById(R.id.textview_uri); } public void onClick_SelectUri(View view) { Intent intent = new Intent(this, UriListActivity.class); startActivityForResult(intent, 1); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1 && resultCode == 1) { mUri = data.getStringExtra("uri"); mSelectUri.setText(mUri); } } @Override public void onNewIntent(Intent intent) { if (mUri == null) { Intent myIntent = new Intent(this, ShowNFCTagContentActivity.class); myIntent.putExtras(intent); myIntent.setAction(NfcAdapter.ACTION_NDEF_DISCOVERED); startActivity(myIntent); } else { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); NdefMessage ndefMessage = new NdefMessage( new NdefRecord[] {createUriRecord(mUri)}); if (writeTag(ndefMessage, tag)) { mUri = null; mSelectUri.setText(""); } } } public NdefRecord createUriRecord(String uriStr) { byte prefix = 0; //从uri前缀集合中找到匹配的前缀,并获得相应的标识代码 for (Byte b : UriRecord.URI_PREFIX_MAP.keySet()) { //将Uri前缀转换成小写 String prefixStr = UriRecord.URI_PREFIX_MAP.get(b).toLowerCase(); //前缀不为空串 if ("".equals(prefixStr)) continue; //比较Uri前缀 if (uriStr.toLowerCase().startsWith(prefixStr)) { //用字节表示的Uri前缀 prefix = b; //截取完整Uri中除了Uri前缀外的其他部分 uriStr = uriStr.substring(prefixStr.length()); break; } } //为存储在标签中的Uri创建一个Byte数组 byte[] data = new byte[1 + uriStr.length()]; //指定第1字节为Uri前缀的标识代码 data[0] = prefix; //将剩余的部分复制到data字节数组中 System.arraycopy(uriStr.getBytes(), 0, data, 1, uriStr.length()); //创建封装uri的NdefRecord对象 NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], data); //返回NdefRecord对象 return record; } boolean writeTag(NdefMessage message, Tag tag) { int size = message.toByteArray().length; try { Ndef ndef = Ndef.get(tag); if (ndef != null) { 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 format = NdefFormatable.get(tag); if (format != null) { try { 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; } } }UriListActivity:
package mobile.android.read.write.uri; import android.app.ListActivity; import android.content.Intent; import android.graphics.Camera; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.SimpleAdapter; public class UriListActivity extends ListActivity implements OnItemClickListener { private String uris[] = new String[] {"http://www.google.com", "http://www.apple.com", "http://developer.apple.com", "http://www.126.com", "ftp://192.168.17.160", "https://192.168.17.120", "smb://192.168.17.100"}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, uris); setListAdapter(arrayAdapter); getListView().setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(); intent.putExtra("uri", uris[position]); setResult(1, intent); finish(); } }ShowNFCTagContentActivity:
package mobile.android.read.write.uri; import mobile.android.read.write.uri.library.UriRecord; import android.app.Activity; 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; public class ShowNFCTagContentActivity extends Activity { private TextView mTagContent; private Tag mDetectedTag; private String 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); mDetectedTag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); Ndef ndef = Ndef.get(mDetectedTag); mTagText = ndef.getType() + "\n最大数据容量:" + ndef.getMaxSize() + " bytes\n\n"; readNFCTag(); mTagContent.setText(mTagText); } private void readNFCTag() { if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { Parcelable[] rawMsgs = getIntent().getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); NdefMessage ndefMessage = null; int contentSize = 0; if (rawMsgs != null) { if (rawMsgs.length > 0) { ndefMessage = (NdefMessage) rawMsgs[0]; contentSize = ndefMessage.toByteArray().length; } else { return; } } try { NdefRecord record = ndefMessage.getRecords()[0]; UriRecord uriRecord = UriRecord .parse(ndefMessage.getRecords()[0]); mTagText += uriRecord.getUri().toString() + "\n\nUri\n" + contentSize + " bytes"; } catch (Exception e) { mTagContent.setText(e.getMessage()); } } } }UriRecord.java
package mobile.android.read.write.uri.library; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import android.net.Uri; import android.nfc.NdefRecord; public class UriRecord { //映射Uri前缀和对应的值 public static final Map<Byte, String> URI_PREFIX_MAP = new HashMap<Byte, String>(); static { //设置NDEF Uri规范支持的Uri前缀,在解析payload时,需要根据payload的第1个字节定位相应的uri前缀 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:"); } private final Uri mUri; private UriRecord(Uri uri) { this.mUri = uri; } //获取已经解析的Uri public Uri getUri() { return mUri; } public static UriRecord parse(NdefRecord record) { //获取TNF short tnf = record.getTnf(); //TNF是TNF_WELL_KNOWN,使用了前缀的Uri if (tnf == NdefRecord.TNF_WELL_KNOWN) { return parseWellKnown(record); } //TNF是TNF_ABSOLUTE_URI,即绝对Uri,不使用前缀 else if (tnf == NdefRecord.TNF_ABSOLUTE_URI) { return parseAbsolute(record); } throw new IllegalArgumentException("Unknown TNF " + tnf); } /** Parse and absolute URI record */ private static UriRecord parseAbsolute(NdefRecord record) { //直接将payload转成uri byte[] payload = record.getPayload(); Uri uri = Uri.parse(new String(payload, Charset.forName("UTF-8"))); return new UriRecord(uri); } /** Parse an well known URI record */ private static UriRecord parseWellKnown(NdefRecord record) { //判断RTD是否为RTD_URI if (!Arrays.equals(record.getType(), NdefRecord.RTD_URI)) return null; byte[] payload = record.getPayload(); /* * payload[0] contains the URI Identifier Code, per the NFC Forum * "URI Record Type Definition" section 3.2.2. * * payload[1]...payload[payload.length - 1] contains the rest of the * URI. */ //payload[0]中包括URI标识代码,也就是URI_PREFIX_MAP中的key //根据Uri标识代码获取Uri前缀 String prefix = URI_PREFIX_MAP.get(payload[0]); //获取Uri前缀占用的字节数 byte[] prefixBytes = prefix.getBytes(Charset.forName("UTF-8")); //为容纳完整的Uri创建一个byte数组 byte[] fullUri = new byte[prefixBytes.length + payload.length - 1]; //将Uri前缀和其余部分组合,形成一个完整的Uri System.arraycopy(prefixBytes, 0, fullUri, 0, prefixBytes.length); System.arraycopy(payload, 1, fullUri, prefixBytes.length, payload.length - 1); //根据解析出来的Uri创建Uri对象 Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8"))); //创建UriRecord对象并返回 return new UriRecord(uri); } }清单文件:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mobile.android.read.write.uri" 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=".ReadWriteUriMainActivity" android:label="读写NFC标签的Uri" 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:scheme="http" /> <data android:scheme="https" /> <data android:scheme="ftp" /> </intent-filter> </activity> <activity android:name=".ShowNFCTagContentActivity" android:label="显示NFC标签内容" /> <activity android:name=".UriListActivity" android:label="选择Uri" /> </application> </manifest>
AAR例子程序:
AutoRunApplicationActivity:
package mobile.android.auto.run.application; import java.net.URI; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.net.Uri; 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.Button; import android.widget.Toast; public class AutoRunApplicationActivity extends Activity { private Button mSelectAutoRunApplication; private String mPackageName; private NfcAdapter mNfcAdapter; private PendingIntent mPendingIntent; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auto_run_application); mSelectAutoRunApplication = (Button) findViewById(R.id.button_select_auto_run_application); //获得默认的NfcAdapter对象 mNfcAdapter = mNfcAdapter.getDefaultAdapter(this); //创建与当前Activity关联的PendingIntent对象 mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0); } //当窗口获得焦点时会提升当前窗口处理NFC标签的优先级 @Override public void onResume() { super.onResume(); //提升当前处理NFC标签的优先级 if (mNfcAdapter != null) mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); } //当窗口的launchMode被设为singleTop时调用方法(不再调用onCreat方法) @Override public void onNewIntent(Intent intent) { //必须先选择一个Package if (mPackageName == null) return; //获取表示当前标签的对象 Tag detectedTag = intent.getParcelableExtra(mNfcAdapter.EXTRA_TAG); //向标签写入Package writeNFCTag(detectedTag); } //当窗口失去焦点后,应恢复Android系统处理NFC标签的默认状态 @Override public void onPause() { super.onPause(); //恢复处理NFC标签的窗口的默认优先级(禁止当前窗口的优先处理NFC标签) if (mNfcAdapter != null) mNfcAdapter.disableForegroundDispatch(this); } //"选择已安装的应用程序"按钮的单击事件方法 public void onClick_SelectAutoRunApplication(View view) { Intent intent = new Intent(this, InstalledApplicationListActivity.class); //显示“已安装应用程序”窗口 startActivityForResult(intent, 0); } //向标签写入数据 public void writeNFCTag(Tag tag) { //必须要指定一个Tag对象 if (tag == null) { Toast.makeText(this, "NFC Tag未建立连接", Toast.LENGTH_LONG).show(); return; } //创建NdefMessage对象 //NdefRecord.creatApplicationRecord方法创建一个封装Package的NdefRecord对象 NdefMessage ndefMessage = new NdefMessage( new NdefRecord[] {NdefRecord .createApplicationRecord(mPackageName)}); //获取NdefMessage对象的尺寸 int size = ndefMessage.toByteArray().length; try { //获取Ndef对象 Ndef ndef = Ndef.get(tag); //处理NDEF格式的数据 if (ndef != null) { //允许对标签进行IO操作,连接 ndef.connect(); //NFC标签不是可写的(只读的) if (!ndef.isWritable()) { Toast.makeText(this, "NFC Tag是只读的!", Toast.LENGTH_LONG) .show(); return; } //NFC标签的空间不足 if (ndef.getMaxSize() < size) { Toast.makeText(this, "NFC Tag的空间不足!", Toast.LENGTH_LONG) .show(); return; } //向NFC标签写入数据 ndef.writeNdefMessage(ndefMessage); Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG).show(); } else { //创建NdefFormatable对象 NdefFormatable format = NdefFormatable.get(tag); if (format != null) { try { //允许标签IO操作,进行连接 format.connect(); //重新格式化NFC标签,并写入数据 format.format(ndefMessage); Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG) .show(); } catch (Exception e) { Toast.makeText(this, "写入NDEF格式数据失败!", Toast.LENGTH_LONG) .show(); } } else { Toast.makeText(this, "NFC标签不支持NDEF格式!", Toast.LENGTH_LONG) .show(); } } } catch (Exception e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == 1) { //更新“选择已安装的应用程序”按钮的显示文本(Package name和label) mSelectAutoRunApplication.setText(data.getExtras().getString( "package_name")); //下面的代码用于提取Package Name String temp = mSelectAutoRunApplication.getText().toString(); mPackageName = temp.substring(temp.indexOf("\n") + 1); } } }InstalledApplicationListActivity:
package mobile.android.auto.run.application; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; public class InstalledApplicationListActivity extends ListActivity implements OnItemClickListener { //用于保存已安装应用程序的Package和Label private List<String> mPackages = new ArrayList<String>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //获得PackageManager对象 PackageManager packageManager = getPackageManager(); //获取系统中已安装的所有应用程序的信息,每一个PackageInfo对象表示一个应用程序 List<PackageInfo> packageInfos = packageManager .getInstalledPackages(PackageManager.GET_ACTIVITIES); //枚举所有的应用程序信息,从中取出Package和应用程序的Label,中间用“\n”分离 for (PackageInfo packageInfo : packageInfos) { //LoadLabel方法返回的值就是定义Activity时的android:label属性值 mPackages.add(packageInfo.applicationInfo.loadLabel(packageManager) + "\n" + packageInfo.packageName); } //创建一个用于操作Package集合的ArrayAdapter对象 ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, mPackages); //在ListView控件中显示所有的Package和程序名 setListAdapter(arrayAdapter); //指定列表项的单击事件方法 getListView().setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(); //当单击列表项时,会通过package_name传回Package和Label intent.putExtra("package_name", mPackages.get(position)); setResult(1, intent); finish(); } }清单文件:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mobile.android.auto.run.application" 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=".AutoRunApplicationActivity" android:label="@string/title_activity_auto_run_application" android:launchMode="singleTop" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".InstalledApplicationListActivity" android:label="@string/title_activity_installed_application_list" android:screenOrientation="portrait" /> </application> </manifest>
通过浏览器自动打开一个网站:
package mobile.android.auto.open.uri; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.net.Uri; 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.widget.Toast; public class AutoOpenUriActivity extends Activity { private NfcAdapter nfcAdapter; private PendingIntent pendingIntent; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auto_open_uri); nfcAdapter = NfcAdapter.getDefaultAdapter(this); pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0); } @Override public void onResume() { super.onResume(); if (nfcAdapter != null) nfcAdapter .enableForegroundDispatch(this, pendingIntent, null, null); } @Override public void onNewIntent(Intent intent) { Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); writeNFCTag(detectedTag); } @Override public void onPause() { super.onPause(); if (nfcAdapter != null) nfcAdapter.disableForegroundDispatch(this); } public void writeNFCTag(Tag tag) { if (tag == null) { Toast.makeText(this, "NFC Tag未建立连接", Toast.LENGTH_LONG).show(); return; } // NdefMessage ndefMessage = new NdefMessage(new NdefRecord[] // { NdefRecord.createUri("http://blog.csdn.net/nokiaguy")}); NdefMessage ndefMessage = new NdefMessage( new NdefRecord[] {NdefRecord.createUri(Uri .parse("http://www.baidu.com"))}); int size = ndefMessage.toByteArray().length; try { Ndef ndef = Ndef.get(tag); if (ndef != null) { ndef.connect(); if (!ndef.isWritable()) { Toast.makeText(this, "NFC Tag是只读的!", Toast.LENGTH_LONG) .show(); return; } if (ndef.getMaxSize() < size) { Toast.makeText(this, "NFC Tag的空间不足!", Toast.LENGTH_LONG) .show(); return; } ndef.writeNdefMessage(ndefMessage); Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG).show(); } else { NdefFormatable format = NdefFormatable.get(tag); if (format != null) { try { format.connect(); format.format(ndefMessage); Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG) .show(); } catch (Exception e) { Toast.makeText(this, "写入NDEF格式数据失败!", Toast.LENGTH_LONG) .show(); } } else { Toast.makeText(this, "NFC标签不支持NDEF格式!", Toast.LENGTH_LONG) .show(); } } } catch (Exception e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); } } }
附上官方教程:http://developer.android.com/guide/topics/connectivity/nfc/nfc.html