接着上一篇文章来说,在前一个文章中,我们为大家简单介绍了一下什么是NFC,在这节内容中,我们为大家介绍一下NFC的读写卡模式的开发。
读写卡模式是通过手机对nfc标签卡信息经行读写操作,但是,在使用前,我们应该去检查一下设置和添加一些权限。
在这里我建议大家将NFC的一些基础操作放入到一个NfcBaseActivity中,这样,我们在使用时就不需要每次都经行重复的操作。
1、检测NFC状态
public int nfcAdapterInitialize() {//自定义的函数
//尝试去获取设备默认的NfcAdapter(NFC适配器)对象,由于手机中一般只有一个NFC设备,所以我们这里获取默认的即可。
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
//判断nfcAdapter是否为空,若为空则手机不支持NFC设备
if (nfcAdapter == null) {
Toast.makeText(this, "不支持nfc", Toast.LENGTH_SHORT).show();
return 0;
} else {//若不为空,则判断NFC是否开启
if (!nfcAdapter.isEnabled()) {
Toast.makeText(this, "nfc没有开启", Toast.LENGTH_SHORT).show();
//我们将跳转到设置页面去开启NFC
startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
return 0;
}
}
return 1;
}
建议以上代码放入NfcBaseActivity的onCreate中,当你的程序需要调用NFC时直接继承即可。
2、NFC前台调度系统
NFC中有两种响应标签的方式,一种是前台调度系统,另一种是intent过滤器的方式。前台调度系统其实就是运行Activity高优先的来处理Tag的技术,前台调用系统的优先级要高于intent过滤器。但前台调度系统要求Activity是被打开的。
首先我们需要在Activity被创建时,去初始化NFC的信息
private NfcAdapter nfcAdapter;
private NfcManager nfcManager;
private PendingIntent pendingIntent;
private IntentFilter[] intentFilters;
private String[][] mTechList;
private void nfcInitialization(){
//该参数的作用是指定用哪个Activity来处理标签
//参数1:上下文
// 参数二 不使用了——0,
// 参数三:一个意图用来存储信息,这里会根据这个意图来调用指定Activity
//FLAG_ACTIVITY_SINGLE_TOP:指定Activity不能被重复创建
// 参数四:对参数的操作标志
pendingIntent= PendingIntent.getActivity(
this,0,
newIntent(this,getClass())
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
,0);
//创建意图过滤器,指定该前台调度系统拦截那些类型的标签
//这里说明IntentFilter不是很熟悉的童鞋先去查一下这里的资料
//在这里我们现在*/*表示拦截所有标签
IntentFilter intentFilter=new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
IntentFilter intentFilter1=new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
try {
intentFilter.addDataType("*/*");
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
//将我们的意图放入到数组中(在我的案例中,intentFilter1其实是没有使用的)
intentFilters=new IntentFilter[]{intentFilter,intentFilter1};
//指定过滤标签,这里填入null就好
mTechList=null;
}
这里我们初始化了前台调度系统的一些参数,下面我们看一下怎么启用前台调度系统
//在activity不可见的时候我们关闭前台调度
@Override
protected void onPause() {
super.onPause();
if(nfcAdapter!=null)
nfcAdapter.disableForegroundDispatch(this);
}
//在Activity显示的时候,我们让NFC前台调度系统处于打开状态
@Override
protected void onResume() {
super.onResume();
Log.w("nfclive","resume"+nfcAdapter);
if(nfcAdapter!=null)
nfcAdapter.enableForegroundDispatch(this,pendingIntent,
intentFilters,mTechList);
}
当完成这些代码后,我们就可以将标签拦截,不在向下传递。
当我们拦截到标签后,我们需要获取标签中的信息,由于我们在前面采用了FLAG_ACTIVITY_SINGLE_TOP,所以在每次刷入标签时不会去调用onCreate而是调用onNewIntent来经行回调,且会将Tag放入到Intent中。下面我们通过一些代码来尝试获取NFC标签中的Uid和Tag对象,并将uid转为字符串
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//这里我们去得到tag对象
tag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
uid= getUid(tag);
//Uid样子:4-106-91-119-72-2-78
}
public static String getUid(Tag tag){
//获取tag中ID的byte数组
byte[] tagByteId=tag.getId();
//通过流来经行解析为字符串
StringBuffer stringBuffer=new StringBuffer();
for (byte b:tagByteId){
stringBuffer.append(b);
}
return stringBuffer.toString();
}
当然,单纯的只获取ID对我们来说可能并不满足,我们想获取一下NFC中的存放了说明数据,接下来让我们去获取一下NFC中的数据。
在看数据之前让我们来看一下NFC中的数据结构吧
Parcelable[] rawmsgs=intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);来获取NdefMessage消息,在这里Parcelable和NdefMessage是实现关系。下面是我的一段代码的实现,我们只讲第一个NdefMessage取出。
public static NdefMessage getNdefMessage(Intent intent) {
NdefMessage[] ndefMessage=null;
Parcelable[] rawmsgs=
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawmsgs!=null){
ndefMessage=new NdefMessage[rawmsgs.length];
for (int i=0;i<rawmsgs.length;i++){
ndefMessage[i]= (NdefMessage) rawmsgs[i];
}
}
return ndefMessage[0];
}
这样我们就可以得到NdefMessage了,但是我们通过这个消息如何解析到我们想要的数据呢?其实在NdefMessage中,又包含了多个NdefRecord,每个NdefRecord就表示一个NFC数据的记录,可以通过message.getRecords()来获取。
NdefRecord[] record=message.getRecords()
然后我们需要解析的是record中的消息如果你是NDEF消息,先把这个东西copy到你的代码中
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:");
}
这是一些头函数代表的含义
接下来我们获取一下NFC中字节流
byte[] bytes=records[0].getPayload();
然后我们来判断一下NDEF的类型records[0].getType();
在NdefRecord定义了
public static final byte[] RTD_ALTERNATIVE_CARRIER ;
public static final byte[] RTD_HANDOVER_CARRIER ;
public static final byte[] RTD_HANDOVER_REQUEST l;
public static final byte[] RTD_HANDOVER_SELECT ;
public static final byte[] RTD_SMART_POSTER ;
public static final byte[] RTD_TEXT ;
public static final byte[] RTD_URI ;
这些格式。RTD_TEXT 是文本格式,RTD_URI是可以直接调用系统。这是比较常用的两个,其中URI又可以指定这些类型
public static final short TNF_ABSOLUTE_URI = 3;//绝对URI
public static final short TNF_EMPTY = 0;
public static final short TNF_EXTERNAL_TYPE = 4;
public static final short TNF_MIME_MEDIA = 2;
public static final short TNF_UNCHANGED = 6;
public static final short TNF_UNKNOWN = 5;
public static final short TNF_WELL_KNOWN = 1;//已知类型,NDEF论坛定义
若我们解析的数据仅为TEXT类型时,只需要将字节的第一位取出,判断编码格式,其他位按照指定编码进行解析
public static String parseWellKonwnUriRecord(NdefMessage message){
NdefRecord[] records=message.getRecords();
byte[]bytes=records[0].getPayload();
byte charData=bytes[0];
byte[] data=new byte[bytes.length-1];
System.arraycopy(bytes,1,data,0,data.length);
LogUtil.w("-------------------------"+new String(data,charData==1?"UTF-8","GBK"));
return new String(data,charData==1?"UTF-8","GBK");
}
RTD_URI 方式和读取字符串类似,只是没有首位的编码位;TNF_WELL_KNOWN 类型时,表示数据为已知类型,从URI_PREFIX_MAP中获取首位对应的字符串即可。
以上就是NFC的读卡模式,下面我们来说一下NFC的写入模式
其实NFC的写卡模式与NFC的读卡模式是相似的。
首先我们需要将信息准备好!如URI,或text信息
然后我们创建一个 NdefRecord record=new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload);
NdefRecord.TNF_WELL_KNOWN:表示已知数据类型
NdefRecord.RTD_TEXT:为指定数据的格式
payload:为准备好的字节数据
然后就可以生成一个NdefRecord,在将多个或一个NdefRecord放入NdefMessage中
new NdefMessage(record,arrRecord);
然后我们在onNewIntent,获取到Ndef对象Ndef ndef=Ndef.get(getTag());
经行提交:ndef.connect();
,接下来判断是否NFC标签的标签是否可以写入
if (ndef.getMaxSize() < message.getByteArrayLength()){
Toast.makeText(this,"NFC标签空间不足",Toast.LENGTH_SHORT).show();
return;
}else if (ndef==null){
Toast.makeText(this,"非NDEF数据",Toast.LENGTH_SHORT).show();
writeTag(ndefMessage, tag);//通过这种方式写入
Toast.makeText(this,"完成",Toast.LENGTH_SHORT).show();
return;
}
ndef.writeNdefMessage(message);