检测到标签后在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信息结构
NdefRecZ喎�"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcmTW0LXEs6PTw7e9t6g8L3N0cm9uZz48YnI+CjwvcD4KPHA+MS6/yc2ouf1OZGVmUmVjb3JkLmdldFRuZigpt723qMC0u/G1w1RORtfWts6juzwvcD4KPHA+Mi7NqLn9TmRlZlJlY29yZC5nZXRUeXBlKCm3vbeowLS78bXDUlRE19a2zqOstbFUTkbOqlRORl9XRUxMX0tOT1dOyrG1xFJURKGjPC9wPgo8cD4zLs2ouf1OZGVmUmVjb3JkLmdldFBheWxvYWQoKbe9t6jAtLvxtcPKtbzKtsHQtLXEyv2+3aGjPC9wPgo8cD48YnI+CjwvcD4KPHA+PGJyPgo8L3A+CjxwPjxzdHJvbmc+TkRFRs7Esb4mIzI2Njg0O8q9PC9zdHJvbmc+PGJyPgo8L3A+CjxwPk5kZWZNZXNzYWdl1tC1xHBheWxhb2S+zcrHyrW8yrXEyv2+3aOsxuTW0E5ERUbOxLG+JiMyNjY4NDvKvc6qo7o8YnI+CjwvcD4KPHA+PGltZyBzcmM9"http://www.2cto.com/uploadfile/Collfiles/20140430/20140430090648250.png" alt="\">
NDEF Uri格式
1、NdefMessage中的paylaod就是实际的数据,其中NDEF文本格式为:
2、前缀需要查表解析
例子程序:
ReadWriteTextMainActivity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
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);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<manifest android:versionname=
"1.0"
android:versioncode=
"1"
package
=
"mobile.android.read.write.text"
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<uses-sdk android:targetsdkversion=
"15"
android:minsdkversion=
"15"
>
<uses-permission android:name=
"android.permission.NFC"
>
<intent-filter>
<category android:name=
"android.intent.category.LAUNCHER"
>
</category>
<intent-filter>
<category android:name=
"android.intent.category.DEFAULT"
>
<data android:mimetype=
"text/plain"
>
</data>
</category>
</activity>
</activity></action></intent-filter></action></intent-filter></activity></application></uses-permission></uses-sdk></manifest>
|