添加權(quán)限
<uses-permission android:name="android.permission.NFC" />
1. 讀寫文本
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="請輸入要寫入的文本"/>
<TextView
android:hint="卡中內(nèi)容"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
/**寫入內(nèi)容的文本框*/
private EditText mEditText;
/**顯示NFC卡中的內(nèi)容*/
private TextView mTextView;
/**NFC相關(guān)*/
/**NFC適配器*/
private NfcAdapter mNfcAdapter;
/**延時意圖*/
private PendingIntent mPendingIntent;
/**檢測的卡類型*/
private static String[][] sTechArr = null;
/**意圖過濾器*/
private static IntentFilter[] sFilters = null;
static {
try {
sTechArr = new String[][]{
{IsoDep.class.getName()}, {NfcV.class.getName()}, {NfcF.class.getName()},
{MifareUltralight.class.getName()}, {NfcA.class.getName()}
};
// 如果這里不設(shè)置過濾器,就必須要在AndroidManifest.xml中為MainActivity設(shè)置過濾器
// 這里的ACTION在NfcAdapter中有四個選中,個人認(rèn)為是在設(shè)置響應(yīng)優(yōu)先級
/**
*
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
*/
sFilters = new IntentFilter[]{
new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED, "*/*")
};
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initNfc();
// 判斷當(dāng)前手機(jī)是否支持NFC
if (null == mNfcAdapter) {
// 如果為null,則為不支持。
Toast.makeText(this, "當(dāng)前設(shè)備不支持NFC功能!", Toast.LENGTH_SHORT).show();
return;
}
}
/**
* 初始化NFC
*/
private void initNfc() {
// 1. 初始化Nfc適配器
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
// 2. 初始化延時意圖
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
@Override
protected void onResume() {
super.onResume();
// 3. 允許后臺喚醒--此處是指應(yīng)用處于不可見的狀態(tài)時,將卡片貼近手機(jī)時,喚醒當(dāng)前App。
// 如果sFilters為null,則為不過濾任何意圖;如果sTechArr為null時,則為接收所有類型的卡片。
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, sFilters, sTechArr);
}
@Override
protected void onPause() {
super.onPause();
// 4. 禁止后臺喚醒
mNfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 5. 重寫OnIntent方法, 讀取到的數(shù)據(jù)存儲在intent中
// 獲取到意圖中的Tag數(shù)據(jù)
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// TAG: Tech [android.nfc.tech.NfcV, android.nfc.tech.Ndef]
Log.e(TAG, "onNewIntent: " + tag);
// 此處判斷輸入框mEditText的內(nèi)容是否為空,如果為空,則為讀取數(shù)據(jù);如果不為空,則為寫入數(shù)據(jù)
if (TextUtils.isEmpty(mEditText.getText().toString())) {
// 數(shù)據(jù)為空,讀取數(shù)據(jù)
readData(tag);
} else {
// 數(shù)據(jù)不為空,寫入數(shù)據(jù)
writeData(tag);
}
}
/**
* 讀取數(shù)據(jù)
* @param tag Tag
*/
private void readData(Tag tag) {
// 從Tag中獲取Ndef信息
Ndef ndef = Ndef.get(tag);
try {
// 連接
ndef.connect();
// readData: type: android.ndef.unknown, size: 250, message: NdefMessage [NdefRecord tnf=1 type=54 payload=027A68E4BDA0E5A5BD]
Log.e(TAG, "readData: type: " + ndef.getType() + ", size: " + ndef.getMaxSize() + ", message: " + ndef.getNdefMessage().toString());
// 獲取NdefMessage消息
NdefMessage ndefMessage = ndef.getNdefMessage();
// 獲取NdefRecord記錄
//ndefMessage.getRecords();
// 遍歷數(shù)組
StringBuilder stringBuilder = new StringBuilder();
// 文本
String text;
for (NdefRecord ndefRecord : ndefMessage.getRecords()) {
text = parseNdefRecord(ndefRecord);
if (!TextUtils.isEmpty(text)) {
stringBuilder.append(text);
}
}
// 設(shè)置文本
mTextView.setText(stringBuilder.toString());
} catch (IOException | FormatException e) {
e.printStackTrace();
}
}
/**
* 解析NdefRecord數(shù)據(jù)
* @param ndefRecord NdefRecord記錄
*/
private String parseNdefRecord(NdefRecord ndefRecord) {
// 判斷是否為文本格式數(shù)據(jù)
if (NdefRecord.TNF_WELL_KNOWN != ndefRecord.getTnf()) {
return "";
}
if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
return "";
}
try {
// 獲得字節(jié)流
byte[] payload = ndefRecord.getPayload();
// 獲得編碼格式
String textEncoding = ((payload[0] & 0x80) == 0) ? "utf-8" : "utf-16";
// 獲得語言編碼長度
int languageCodeLength = payload[0] & 0x3f;
// 語言編碼
String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
// 獲取文本
return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength -1, textEncoding);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}
/**
* 寫數(shù)據(jù)
* @param tag Tag
*/
private void writeData(Tag tag) {
// 創(chuàng)建一個NdefMessage消息
NdefMessage ndefMessage = new NdefMessage(createTextRecord(mEditText.getText().toString()));
// 寫入Tag
if (writeTag(ndefMessage, tag)) {
mEditText.setText("");
Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "寫入失敗", Toast.LENGTH_SHORT).show();
}
}
/**
* 將NdefMessage消息寫入Tag
* @param ndefMessage Ndef消息
* @param tag Tag
*/
private boolean writeTag(NdefMessage ndefMessage, Tag tag) {
try {
// 獲取Ndef
Ndef ndef = Ndef.get(tag);
// 連接
ndef.connect();
// 寫入數(shù)據(jù)
ndef.writeNdefMessage(ndefMessage);
return true;
} catch (IOException | FormatException e) {
e.printStackTrace();
}
return false;
}
/**
* 創(chuàng)建文本信息
* @param text 要寫入的文本信息
*/
private NdefRecord createTextRecord(String text) {
// 設(shè)置語言
byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));
// 設(shè)置編碼
Charset utfEncoding = Charset.forName("UTF-8");
// 要寫入的二進(jìn)制數(shù)據(jù)
byte[] textBytes = text.getBytes(utfEncoding);
int utfBit = 0;
// 將語言長度轉(zhuǎn)化對應(yīng)的字符
char status = (char)(utfBit + langBytes.length);
// 創(chuàng)建一個大小為 1(即語言長度轉(zhuǎn)化對應(yīng)的字符) + 語言字節(jié)長度 + 文本字節(jié)長度的字節(jié)數(shù)組
byte[] data = new byte[1 + langBytes.length + textBytes.length];
// 寫入第一位數(shù)據(jù)
data[0] = (byte) status;
// 寫入語言字節(jié)
System.arraycopy(langBytes, 0, data, 1, langBytes.length);
// 寫入文本數(shù)據(jù)
System.arraycopy(textBytes, 0, data, langBytes.length + 1, textBytes.length);
// 創(chuàng)建一個NdefRecord記錄
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
}
/**
* 初始化布局
*/
private void initView() {
mEditText = (EditText) this.findViewById(R.id.editText);
mTextView = (TextView) this.findViewById(R.id.textView);
}
}
2. NFC讀寫Uri
對NFC的配置信息相同,此處只是更改讀取與寫入的操作。
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 5. 重寫OnIntent方法, 讀取到的數(shù)據(jù)存儲在intent中
// 獲取到意圖中的Tag數(shù)據(jù)
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// TAG: Tech [android.nfc.tech.NfcV, android.nfc.tech.Ndef]
Log.e(TAG, "onNewIntent: " + tag);
// 此處判斷輸入框mEditText的內(nèi)容是否為空,如果為空,則為讀取數(shù)據(jù);如果不為空,則為寫入數(shù)據(jù)
if (TextUtils.isEmpty(mEditText.getText().toString())) {
// 數(shù)據(jù)為空,讀取數(shù)據(jù)
readData(tag);
} else {
// 數(shù)據(jù)不為空,寫入數(shù)據(jù)
writeData(tag);
}
}
/**
* 寫入Uri
* http://www.baidu.com/
* @param tag
*/
private void writeData(Tag tag) {
// 創(chuàng)建一個Uri
Uri uri = Uri.parse(mEditText.getText().toString());
// 創(chuàng)建一個NdefMessage格式數(shù)據(jù)
NdefMessage ndefMessage = new NdefMessage(NdefRecord.createUri(uri));
if (writeTag(ndefMessage, tag)) {
Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();
mEditText.setText("");
}
}
/**
* 寫入數(shù)據(jù)
* @param ndefMessage Ndef格式數(shù)據(jù)
* @param tag Tag
* @return true,寫入成功;false,寫入失敗
*/
private boolean writeTag(NdefMessage ndefMessage, Tag tag) {
// 計(jì)算寫入數(shù)據(jù)的長度
int size = ndefMessage.toByteArray().length;
try {
// 獲取Ndef
Ndef ndef = Ndef.get(tag);
if (null != ndef) {
// 連接NFC卡片
ndef.connect();
// 判斷NFC芯片是否可寫
if (!ndef.isWritable()) {
Toast.makeText(this, "卡片不可寫", Toast.LENGTH_SHORT).show();
return false;
}
// 判斷要寫入的數(shù)據(jù)是否大于NFC芯片最大字節(jié)數(shù)
if (ndef.getMaxSize() < size) {
Toast.makeText(this, "寫入數(shù)據(jù)過大", Toast.LENGTH_SHORT).show();
return false;
}
ndef.writeNdefMessage(ndefMessage);
return true;
} else {
// 獲取一個格式化工具
NdefFormatable formatable = NdefFormatable.get(tag);
// 判斷是否為空
if (null != formatable){
// 連接
formatable.connect();
// 格式化并寫入數(shù)據(jù)
formatable.format(ndefMessage);
Toast.makeText(this, "格式化成功,并成功寫入數(shù)據(jù)", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, "無法格式化", Toast.LENGTH_SHORT).show();
}
}
} catch (IOException | FormatException e) {
e.printStackTrace();
}
return false;
}
/**
* 讀取數(shù)據(jù)
* @param tag Tag
*/
private void readData(Tag tag) {
try {
// 獲取Ndef
Ndef ndef = Ndef.get(tag);
// 連接
ndef.connect();
Log.e(TAG, "readData: type: " + ndef.getType() + ", size: " + ndef.getMaxSize()
+ ", message: " + ndef.getNdefMessage().toString());
// 獲取Ndef格式數(shù)據(jù)
NdefMessage ndefMessage = ndef.getNdefMessage();
// 獲取信息中的記錄
NdefRecord[] records = ndefMessage.getRecords();
// 判斷是否有數(shù)據(jù)
if (null != records && records.length > 0) {
StringBuilder stringBuilder = new StringBuilder();
// 迭代
for (NdefRecord ndefRecord : records) {
Uri uri = ndefRecord.toUri();
if (null != uri) {
stringBuilder.append(uri.toString());
}
}
String string = stringBuilder.toString();
mTextView.setText(string);
if (string.startsWith("http://")) {
// 跳轉(zhuǎn)網(wǎng)頁
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(stringBuilder.toString())));
}
}
// 斷開連接
ndef.close();
} catch (IOException | FormatException e) {
e.printStackTrace();
}
}
3. 讀寫非Ndef格式數(shù)據(jù)
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 5. 重寫OnIntent方法, 讀取到的數(shù)據(jù)存儲在intent中
// 獲取到意圖中的Tag數(shù)據(jù)
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// 可打印出當(dāng)前NFC芯片支持的NFC類型
// onNewIntent: TAG: Tech [android.nfc.tech.NfcA, android.nfc.tech.MifareUltralight, android.nfc.tech.NdefFormatable]
Log.e(TAG, "onNewIntent: " + tag);
// 此處判斷輸入框mEditText的內(nèi)容是否為空,如果為空,則為讀取數(shù)據(jù);如果不為空,則為寫入數(shù)據(jù)
if (TextUtils.isEmpty(mEditText.getText().toString())) {
// 數(shù)據(jù)為空,讀取數(shù)據(jù)
readData(tag);
} else {
// 數(shù)據(jù)不為空,寫入數(shù)據(jù)
writeData(tag);
}
}
/**
* 寫數(shù)據(jù)
* @param tag Tag
*/
private void writeData(Tag tag) {
// 獲取到MifareUltralight
MifareUltralight ultralight = MifareUltralight.get(tag);
try {
// 連接
ultralight.connect();
// Write 1 page (4 bytes).
// 往每一頁寫數(shù)據(jù),最多寫四個字節(jié)
ultralight.writePage(4,"中國".getBytes(Charset.forName("gb2312")));
ultralight.writePage(5,"美國".getBytes(Charset.forName("gb2312")));
ultralight.writePage(6,"英國".getBytes(Charset.forName("gb2312")));
ultralight.writePage(7,"法國".getBytes(Charset.forName("gb2312")));
Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();
mEditText.setText("");
// 關(guān)閉連接
ultralight.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 讀數(shù)據(jù)
* @param tag Tag
*/
private void readData(Tag tag) {
// 獲取連接
MifareUltralight ultralight = MifareUltralight.get(tag);
try {
// 連接
ultralight.connect();
byte[] bytes = ultralight.readPages(4);
mTextView.setText(new String(bytes, Charset.forName("gb2312")));
// 斷開連接
ultralight.close();
} catch (IOException e) {
e.printStackTrace();
}
}
4. AndroidBeam打開其他手機(jī)應(yīng)用
/**
* 測試方法, 將兩部手機(jī)靠近,AndroidBeam接觸時,兩部手機(jī)界面同時縮小居中,輕觸處理數(shù)據(jù)的手機(jī)一方,即可完成發(fā)送,
* 放松數(shù)據(jù)期間手機(jī)不能離開,離開之后即會斷開連接,終止發(fā)送。
*/
public class MainActivity extends AppCompatActivity implements
NfcAdapter.CreateNdefMessageCallback, NfcAdapter.OnNdefPushCompleteCallback{
private static final String TAG = "MainActivity";
private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
// 設(shè)置發(fā)送消息的回調(diào)
mNfcAdapter.setNdefPushMessageCallback(this, this);
// 設(shè)置發(fā)送完成回調(diào)
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(this, "NFC靠近", Toast.LENGTH_SHORT).show();
}
/**
* 當(dāng)有NFC靠近時調(diào)用該方法
*/
@Override
public NdefMessage createNdefMessage(NfcEvent event) {
// 將"com.mazaiting.nfc2"更換為另一部手機(jī)中已存在的應(yīng)用包名
return new NdefMessage(NdefRecord.createApplicationRecord("com.mazaiting.nfc2"));
}
/**
* 數(shù)據(jù)發(fā)送完成時調(diào)用方法
*/
@Override
public void onNdefPushComplete(NfcEvent event) {
Toast.makeText(this, "發(fā)送完成", Toast.LENGTH_SHORT).show();
}
@Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,
null,null);
}
@Override
protected void onPause() {
super.onPause();
mNfcAdapter.disableForegroundDispatch(this);
}
}
AndroidBeam接觸時如下圖:

圖1.png
5. AndroidBeam傳送大文件
1). 添加權(quán)限,一定不能忘記
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2). 在assets文件夾放一張圖片或者文件,或者直接指定手機(jī)中的文件地址。
public class MainActivity extends AppCompatActivity implements
NfcAdapter.CreateBeamUrisCallback, NfcAdapter.OnNdefPushCompleteCallback {
private static final String TAG = "MainActivity";
private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;
private final String targetFilename = "/sdcard/image.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
mPendingIntent = PendingIntent.getActivity(this,0,
new Intent(this,getClass()),0);
try{
InputStream is = getResources().getAssets().open("image.jpg");
FileOutputStream fos = new FileOutputStream(targetFilename);
byte[] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
fos.close();
is.close();
}catch (Exception e){
e.printStackTrace();
}
// 設(shè)置回調(diào)
mNfcAdapter.setBeamPushUrisCallback(this, this);
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
@Override
public Uri[] createBeamUris(NfcEvent event) {
Uri[] uris = new Uri[1];
Uri uri = Uri.parse("file://" + targetFilename);
uris[0]=uri;
return uris;
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(this, "接觸", Toast.LENGTH_SHORT).show();
}
@Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,
null,null);
}
@Override
protected void onPause() {
super.onPause();
mNfcAdapter.disableForegroundDispatch(this);
}
@Override
public void onNdefPushComplete(NfcEvent event) {
// 另一部手機(jī)接收的文件位置--/storage/emulated/0/beam/傳輸?shù)奈募? Log.e(TAG, "onNdefPushComplete: ");
}
}