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);
  • 1

然后我们在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);

猜你喜欢

转载自blog.csdn.net/weixin_37734988/article/details/93510825