NFC:全称是近场通信(Near Field Communication),是一种短距离无线技术
Android Beam是一个基于近场通信所做的新功能,这个功能可以为其他手机分享你正在使用的功能。 Android升级到4.1后,Android Beam现在可以在两台支持NFC的Android设备间分享照片和视频,还可以与支持NFC的蓝牙设备相连。
Android NFC同时支持三个主要的操作模式:
1.设备读/写模式,允许NFC设备的读/写NFC目标设备;
2.P2P模式,使NFC设备与其他NFC节点交换数据;这种运作模式被使用在Android Beam中;
3/卡仿真模式,使NFC设备本身作为一个NFC卡。然后模拟NFC卡可以通过一个外部的NFC读写访问,如销售终端NFC点。
4.NDEF数据
从NFC便签读取NDEF格式的数据
向NFC标签写入NDEF格式的数据
通过Android Beam技术将NDEF数据发送到另一部NFC设备
5.NFC的三重过滤机制intent-filter
两个终端设备要想读写数据,会有个短暂配对的时间,数据接收端会根据具体的数据格式和标签类型调用相应的Activity(Tag Dispatch),这个activity需要定义一个intent filter中指定不同的过滤机制,分三个等级,所以叫NFC的三重过滤机制
5.1NDEF_DISCOVERED
只过滤固定格式的NDEF数据,比如纯文本,指定协议(HTTP FTP SMB等)的URI
5.2TECH_DISCOVERED
当ACTION_NDEF_DISCOVERED指定的过滤机制无法匹配Tag时,就会使用这种过滤机制进行匹配,这种过滤机制并不是通过Tag的数据进行匹配的,而是根据Tag支持的数据存储格式进行匹配,因此这种机制使用范围很广
5.3TAG_DISCOVERED
这种机制用来处理未识别的Tag
接入流程:
1.首选NFC依赖硬件,这个就需要权限支持
<uses-permission android:name="android.permission.NFC"/>
外获取NFC设备数据需要在<activity/>
内添加如下内容
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter"/>
nfc_tech_filter
是在res/xml文件下的自定义xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
</tech-list>
</resources>
2.三种模式的Demo运行
NFC的数据NfcAdapter来管理的,NfcAdapter有两种途径获取
NfcManager manager=(NfcManager)getSystemService(Context.NFC_SERVICE); NfcAdapter adapter= manager.getDefaultAdapter();
这是通过NFCmanager获取,
NfcManger的构造器 也是通过NcfAdapter.getNfcAdapter(Context)获取adapter的实例。
同理:我们也可以直接获取NfcAdapter,不通过NfcManger来获取。
NcfAdapter adapter=NfcAdapter.getDefaultAdapter(Context context)
这个adapter其实也是需要通过NfcManager来获取,直接调用静态方法,针对不熟悉getSystemService来说,可以直接使用封装现成的。
adapter的内部会有一个:
static HashMap<Context, NfcAdapter> sNfcAdapters = new HashMap(); //guard by NfcAdapter.class
,所以NFC是独立于Activity,因为adapter内部有一个静态变量,会把当前山下文使用的nfcadapter缓存起来。
2.Tag接收页面的启动模式
<activity
android:name=".nfc.NfcHomeActivity"
android:label="NFC主业"
android:configChanges="screenSize"
android:launchMode="singleTask"
android:immersive="true"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
launchMode:因为NFC是一个独立特性,所有页面启动需要保持一个,singleTask或者
singleTop
NdefMessage:
主要是描写叙述NDEF格式的信息
NdefRecord:
这个是秒速NDEF信息的一个信息段
这两个都是Android NCF技术的核心类,不管是读写NFC标签还是通过Android Beam技术传递数据都须要这两个类
小牛刀
1.获取Tag对象
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
正常该intent通过Activity这个方法获取:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
因为任何页面被打开多少次,都会执行onNewIntent方法
2.推断NFC标签的数格式
Ndef ndef = Ndef.get(tag);
3.写入数据
ndef.wrriteNdefMessage(ndefMessage);
4.NdefRecord:NDEF格式数据,创建对象提供如下
public static NdefRecord createApplicationRecord(String packageName)
public static NdefRecord createUri(Uri uri)
public static NdefRecord createUri(String uriString)
public static NdefRecord createMime(String mimeType, byte[] mimeData)
public static NdefRecord createExternal(String domain, String type, byte[] data)
public static NdefRecord createTextRecord(String languageCode, String text)
public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload)
public NdefRecord(byte[] data)
Uri支持格式如下:
private static final String[] URI_PREFIX_MAP = new String[] {
"", // 0x00
"http://www.", // 0x01
"https://www.", // 0x02
"http://", // 0x03
"https://", // 0x04
"tel:", // 0x05
"mailto:", // 0x06
"ftp://anonymous:anonymous@", // 0x07
"ftp://ftp.", // 0x08
"ftps://", // 0x09
"sftp://", // 0x0A
"smb://", // 0x0B
"nfs://", // 0x0C
"ftp://", // 0x0D
"dav://", // 0x0E
"news:", // 0x0F
"telnet://", // 0x10
"imap:", // 0x11
"rtsp://", // 0x12
"urn:", // 0x13
"pop:", // 0x14
"sip:", // 0x15
"sips:", // 0x16
"tftp:", // 0x17
"btspp://", // 0x18
"btl2cap://", // 0x19
"btgoep://", // 0x1A
"tcpobex://", // 0x1B
"irdaobex://", // 0x1C
"file://", // 0x1D
"urn:epc:id:", // 0x1E
"urn:epc:tag:", // 0x1F
"urn:epc:pat:", // 0x20
"urn:epc:raw:", // 0x21
"urn:epc:", // 0x22
"urn:nfc:", // 0x23
};
三、核心业务
1.action的校验
String action = intent.getAction();
if (TextUtils.equals(action, NfcAdapter.ACTION_TAG_DISCOVERED)) {
return true;
}
2.读
private void readNfcTag(Intent intent) {
if (intent == null)
return;
Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (parcelables == null || parcelables.length == 0)
return;
NdefMessage mNdefMsg = (NdefMessage) parcelables[0];
NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
if (mNdefRecord != null) {
try {
String msg = new String(mNdefRecord.getPayload(), "utf-8");
} catch (Exception e) {
}
}
}
3.写
//NFC写入
private void writeNFC(Tag tag) {
//null不执行操作,强调敲代码的逻辑性
if (tag == null) {
return;
}
NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord.createApplicationRecord(mPackNmae)});
//获得写入大小
int size = ndefMessage.toByteArray().length;
//2.推断是否是NDEF标签
try {
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
//说明是NDEF标签,開始连接
ndef.connect();
//推断是否可写
if (!ndef.isWritable()) {
showToast("当前设备不支持写入");
return;
}
//推断大小
if (ndef.getMaxSize() < size) {
showToast("容量太小了");
return;
}
//写入
ndef.writeNdefMessage(ndefMessage);
showToast("写入成功");
}
} catch (Exception e) {
e.printStackTrace();
}
}
加密卡芯片的读写MifareClassic
MifareClassic:
“MIFARE Classic是恩智浦半导体开发的可用于非接触式智能卡,符合ISO/IEC 14443 A类标准。用于公共交通票证等应用,还可用于各类其他应用有S20,S50(M1),S70几种规格,主要是根据存储器容量划分,存储器容量分别有320B,1K,4K
这种卡和其他的不同,是有密码校验,不同厂家可以定制不同的密码和数据格式,简单的介绍如下
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
MifareClassic mifareClassic = MifareClassic.get(tag);
try {
boolean connected = mifareClassic.isConnected();
if (connected) {
int count = mifareClassic.getSectorCount();
for (int index = 0; index < count; index++) {
//获取善区数
boolean keyAopen = mifareClassic.authenticateSectorWithKeyA(index, MifareClassic.KEY_DEFAULT);
if (keyAopen) {
//获取扇区里面块的数量
int bCount = mifareClassic.getBlockCountInSector(index);
int bIndex = mifareClassic.sectorToBlock(index);
for (int position = 0; position < bCount; position++) {
byte[] data = mifareClassic.readBlock(bIndex + position);//进行了读卡
msgBuffer.append("块" + (bIndex + position) + "数据:").append(ByteArrayToHexString(data)).append("\r\n");
//修改KeyA和KeyB
if ((bIndex + position) == (4 * index + 3)) {
//将所有扇区的最后一个Block修改为111111111111ff078069111111111111
mifareClassic.writeBlock(bIndex + position, new byte[]{(byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0xff, 0x07, (byte) 0x80, (byte) 0x69, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11});
// Log.e("onNewIntent:",(bIndex+position)+"块加密成功");
}
}
}
}
}
} catch (Exception e) {
}
关于Mifare Classic也是用的比较多的地方,暂时先写到这,以后将会整理一份详细的介绍。