添加权限
<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="卡中内容"
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";
/**写入内容的文本框*/
private EditText mEditText;
/**显示NFC卡中的内容*/
private TextView mTextView;
/**NFC相关*/
/**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()}
};
// 如果这里不设置过滤器,就必须要在AndroidManifest.xml中为MainActivity设置过滤器
// 这里的ACTION在NfcAdapter中有四个选中,个人认为是在设置响应优先级
/**
*
<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();
// 判断当前手机是否支持NFC
if (null == mNfcAdapter) {
// 如果为null,则为不支持。
Toast.makeText(this, "当前设备不支持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. 允许后台唤醒--此处是指应用处于不可见的状态时,将卡片贴近手机时,唤醒当前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方法, 读取到的数据存储在intent中
// 获取到意图中的Tag数据
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// TAG: Tech [android.nfc.tech.NfcV, android.nfc.tech.Ndef]
Log.e(TAG, "onNewIntent: " + tag);
// 此处判断输入框mEditText的内容是否为空,如果为空,则为读取数据;如果不为空,则为写入数据
if (TextUtils.isEmpty(mEditText.getText().toString())) {
// 数据为空,读取数据
readData(tag);
} else {
// 数据不为空,写入数据
writeData(tag);
}
}
/**
* 读取数据
* @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();
// 遍历数组
StringBuilder stringBuilder = new StringBuilder();
// 文本
String text;
for (NdefRecord ndefRecord : ndefMessage.getRecords()) {
text = parseNdefRecord(ndefRecord);
if (!TextUtils.isEmpty(text)) {
stringBuilder.append(text);
}
}
// 设置文本
mTextView.setText(stringBuilder.toString());
} catch (IOException | FormatException e) {
e.printStackTrace();
}
}
/**
* 解析NdefRecord数据
* @param ndefRecord NdefRecord记录
*/
private String parseNdefRecord(NdefRecord ndefRecord) {
// 判断是否为文本格式数据
if (NdefRecord.TNF_WELL_KNOWN != ndefRecord.getTnf()) {
return "";
}
if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
return "";
}
try {
// 获得字节流
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 "";
}
/**
* 写数据
* @param tag Tag
*/
private void writeData(Tag tag) {
// 创建一个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();
// 写入数据
ndef.writeNdefMessage(ndefMessage);
return true;
} catch (IOException | FormatException e) {
e.printStackTrace();
}
return false;
}
/**
* 创建文本信息
* @param text 要写入的文本信息
*/
private NdefRecord createTextRecord(String text) {
// 设置语言
byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));
// 设置编码
Charset utfEncoding = Charset.forName("UTF-8");
// 要写入的二进制数据
byte[] textBytes = text.getBytes(utfEncoding);
int utfBit = 0;
// 将语言长度转化对应的字符
char status = (char)(utfBit + langBytes.length);
// 创建一个大小为 1(即语言长度转化对应的字符) + 语言字节长度 + 文本字节长度的字节数组
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, langBytes.length + 1, textBytes.length);
// 创建一个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方法, 读取到的数据存储在intent中
// 获取到意图中的Tag数据
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// TAG: Tech [android.nfc.tech.NfcV, android.nfc.tech.Ndef]
Log.e(TAG, "onNewIntent: " + tag);
// 此处判断输入框mEditText的内容是否为空,如果为空,则为读取数据;如果不为空,则为写入数据
if (TextUtils.isEmpty(mEditText.getText().toString())) {
// 数据为空,读取数据
readData(tag);
} else {
// 数据不为空,写入数据
writeData(tag);
}
}
/**
* 写入Uri
* http://www.baidu.com/
* @param tag
*/
private void writeData(Tag tag) {
// 创建一个Uri
Uri uri = Uri.parse(mEditText.getText().toString());
// 创建一个NdefMessage格式数据
NdefMessage ndefMessage = new NdefMessage(NdefRecord.createUri(uri));
if (writeTag(ndefMessage, tag)) {
Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
mEditText.setText("");
}
}
/**
* 写入数据
* @param ndefMessage Ndef格式数据
* @param tag Tag
* @return true,写入成功;false,写入失败
*/
private boolean writeTag(NdefMessage ndefMessage, Tag tag) {
// 计算写入数据的长度
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;
}
// 判断要写入的数据是否大于NFC芯片最大字节数
if (ndef.getMaxSize() < size) {
Toast.makeText(this, "写入数据过大", Toast.LENGTH_SHORT).show();
return false;
}
ndef.writeNdefMessage(ndefMessage);
return true;
} else {
// 获取一个格式化工具
NdefFormatable formatable = NdefFormatable.get(tag);
// 判断是否为空
if (null != formatable){
// 连接
formatable.connect();
// 格式化并写入数据
formatable.format(ndefMessage);
Toast.makeText(this, "格式化成功,并成功写入数据", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, "无法格式化", Toast.LENGTH_SHORT).show();
}
}
} catch (IOException | FormatException e) {
e.printStackTrace();
}
return false;
}
/**
* 读取数据
* @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格式数据
NdefMessage ndefMessage = ndef.getNdefMessage();
// 获取信息中的记录
NdefRecord[] records = ndefMessage.getRecords();
// 判断是否有数据
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://")) {
// 跳转网页
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(stringBuilder.toString())));
}
}
// 断开连接
ndef.close();
} catch (IOException | FormatException e) {
e.printStackTrace();
}
}
3. 读写非Ndef格式数据
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 5. 重写OnIntent方法, 读取到的数据存储在intent中
// 获取到意图中的Tag数据
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// 可打印出当前NFC芯片支持的NFC类型
// onNewIntent: TAG: Tech [android.nfc.tech.NfcA, android.nfc.tech.MifareUltralight, android.nfc.tech.NdefFormatable]
Log.e(TAG, "onNewIntent: " + tag);
// 此处判断输入框mEditText的内容是否为空,如果为空,则为读取数据;如果不为空,则为写入数据
if (TextUtils.isEmpty(mEditText.getText().toString())) {
// 数据为空,读取数据
readData(tag);
} else {
// 数据不为空,写入数据
writeData(tag);
}
}
/**
* 写数据
* @param tag Tag
*/
private void writeData(Tag tag) {
// 获取到MifareUltralight
MifareUltralight ultralight = MifareUltralight.get(tag);
try {
// 连接
ultralight.connect();
// Write 1 page (4 bytes).
// 往每一页写数据,最多写四个字节
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("");
// 关闭连接
ultralight.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读数据
* @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打开其他手机应用
/**
* 测试方法, 将两部手机靠近,AndroidBeam接触时,两部手机界面同时缩小居中,轻触处理数据的手机一方,即可完成发送,
* 放松数据期间手机不能离开,离开之后即会断开连接,终止发送。
*/
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);
// 设置发送消息的回调
mNfcAdapter.setNdefPushMessageCallback(this, this);
// 设置发送完成回调
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(this, "NFC靠近", Toast.LENGTH_SHORT).show();
}
/**
* 当有NFC靠近时调用该方法
*/
@Override
public NdefMessage createNdefMessage(NfcEvent event) {
// 将"com.mazaiting.nfc2"更换为另一部手机中已存在的应用包名
return new NdefMessage(NdefRecord.createApplicationRecord("com.mazaiting.nfc2"));
}
/**
* 数据发送完成时调用方法
*/
@Override
public void onNdefPushComplete(NfcEvent event) {
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);
}
}
AndroidBeam接触时如下图:
图1.png
5. AndroidBeam传送大文件
1). 添加权限,一定不能忘记
<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文件夹放一张图片或者文件,或者直接指定手机中的文件地址。
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();
}
// 设置回调
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) {
// 另一部手机接收的文件位置--/storage/emulated/0/beam/传输的文件名
Log.e(TAG, "onNdefPushComplete: ");
}
}