程诺陪你学Android之nfc

Android NFC读MifareClassic卡获取卡片ID

2019-02-19  本文已影响13人  程思扬

1.首先要在AndroidManifest.xml中声明如下配置信息:

为了能够使用Android手机的NFC功能,需要在Manifest文件中添加相应的权限:

详细配置请参考-->Android NFC标签读写配置过滤器总结

    <uses-permission android:name="android.permission.NFC" />
    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

2.NFC TAG的发布系统:

当android设备扫描到一个NFC标签时,会自动寻找最适合的Activity来处理这个TAG,如果有多个Activity满足条件的话,会让用户来选择到底使用哪一个Activity来处理,可以理解为就是简单的事件响应与事件处理。

那么如何让一个Activity监听 ”当扫描到NFC标签时” 的这一个事件呢?使用intent filter。

可以理解为当检测到一个NFC标签时,系统自动创建一个相关Intent对象,含有响应intent-filter的Activity将处理这个Intent。

其中,intent filter声明如下:(在AndroidManifest.xml中声明在你需要捕获这个Intent的Activity里)(如下是识别公交卡的TECH格式过滤标签)即ACTION_TECH_DISCOVERED类型的过滤器:

    <activity android:name=".NFCActivity"  android:launchMode="singleInstance">
            <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" /> 
    </activity>

配置android:name=".NFCActivity"是为了当退出app时只要扫描卡片能直接打开app并定位到NFC扫描界面。

在res文件夹下新建一个xml的文件夹,里面放的是Android支持的NFC类型的配置数据。nfc_tech_filter.xml如下:

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <!-- 可以处理所有Android支持的NFC类型 -->
        <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.NfcF</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.NfcV</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.NdefFormatable</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
        </tech-list>
        <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
        </tech-list>
    </resources>

另外还有ACTION_NDEF_DISCOVERED类型的过滤器

    <intent-filter>
                    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="text/plain" />
    </intent-filter>

ACTION_TAG_DISCOVERED类型的过滤器

    <intent-filter>
           <action android:name="android.nfc.action.TAG_DISCOVERED"/>
           <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>

3.详细代码如下:

res/layout/nfc_info.xml

    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/app_background"
        >
    <LinearLayout 
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
      <AbsoluteLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:clickable="true"
            >
            <TextView 
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:textSize="@dimen/enfore_title"
                android:textColor="@color/enfore_title"
                android:layout_x="0dp"
                android:layout_y="0dp"
                android:text="NFC测试"
                android:gravity="center"
                />
            <com.golden.test.iconfont.IconFontTextview     
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:textSize="@dimen/enfore_title"
                   android:textColor="@drawable/back_btn"
                   android:paddingTop="15dp"
                    android:paddingLeft="10dp"
                   android:text=""
                   android:clickable="true"
                   android:gravity="left|center_vertical"
                   android:layout_x="0dp"
                   android:layout_y="0dp"
                   android:onClick="btn_back"
                />
        </AbsoluteLayout>
      
          <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_margin="5dp"
              >
               <TextView 
                  android:id="@+id/promt"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:textColor="@color/enfore_title"
                  android:text="NFC扫描中..."
                  android:textSize="@dimen/app_info"
                  />
          </LinearLayout>
          
    </LinearLayout>
    </ScrollView>

NFCActivity.java

    import android.app.Activity;
    import android.app.PendingIntent;
    import android.content.Intent;
    import android.nfc.NfcAdapter;
    import android.nfc.Tag;
    import android.nfc.tech.MifareClassic;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.TextView;
    import android.widget.Toast;
     
    public class NFCActivity extends Activity {
        NfcAdapter nfcAdapter;
        TextView promt;
        private PendingIntent pi;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.nfc_info);
            promt = (TextView) findViewById(R.id.promt);
            // 获取默认的NFC控制器
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
            if (nfcAdapter == null) {
                Toast.makeText(this, "对不起,您的设备不支持nfc功能!", Toast.LENGTH_SHORT).show();
                //promt.setText("设备不支持NFC!");
                finish();
                return;
            }
            if (!nfcAdapter.isEnabled()) {
                Toast.makeText(this, "请在系统设置中开启NFC功能!", Toast.LENGTH_SHORT).show();
                //promt.setText("请在系统设置中先启用NFC功能!");
                finish();
                return;
            }
        }
     
        public void btn_back(View view){
            this.finish();
        }
        
        @Override
          protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
            // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
            if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
              processIntent(intent);
            }
          }
     
          //页面获取焦点
          @Override
          protected void onResume() {
            super.onResume();
            nfcAdapter.enableForegroundDispatch(this, pi, null, null);
          }
        //页面失去焦点
          @Override
              protected void onPause() {
                  super.onPause();
                  if(nfcAdapter!=null){
                      nfcAdapter.disableForegroundDispatch(this);//关闭前台发布系统
                  }
           }
        /*@Override
        protected void onResume() {
            super.onResume();
            //得到是否检测到ACTION_TECH_DISCOVERED触发
            if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(getIntent().getAction())) {
                //处理该intent
                processIntent(getIntent());
            }
        }*/
        //字符序列转换为16进制字符串
        private String bytesToHexString(byte[] src) {
            StringBuilder stringBuilder = new StringBuilder("0x");
            if (src == null || src.length <= 0) {
                return null;
            }
            char[] buffer = new char[2];
            for (int i = 0; i < src.length; i++) {
                buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
                buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
                System.out.println(buffer);
                stringBuilder.append(buffer);
            }
            return stringBuilder.toString();
        }
        private String ByteArrayToHexString(byte[] inarray) {
            int i, j, in;
            String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
                "B", "C", "D", "E", "F" };
            String out = "";
            for (j = 0; j < inarray.length; ++j) {
              in = (int) inarray[j] & 0xff;
              i = (in >> 4) & 0x0f;
              out += hex[i];
              i = in & 0x0f;
              out += hex[i];
            }
            return out;
          }
        /**
         * Parses the NDEF Message from the intent and prints to the TextView
         */
        private void processIntent(Intent intent) {
            //取出封装在intent中的TAG
            Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            String CardId =ByteArrayToHexString(tagFromIntent.getId());
            String metaInfo = "";
            metaInfo+="卡片ID:"+CardId;
            for (String tech : tagFromIntent.getTechList()) {
                System.out.println(tech);
            }
            boolean auth = false;
            //读取TAG
            MifareClassic mfc = MifareClassic.get(tagFromIntent);
            try {
                //Enable I/O operations to the tag from this TagTechnology object.
                mfc.connect();
                int type = mfc.getType();//获取TAG的类型
                int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
                String typeS = "";
                switch (type) {
                case MifareClassic.TYPE_CLASSIC:
                    typeS = "TYPE_CLASSIC";
                    break;
                case MifareClassic.TYPE_PLUS:
                    typeS = "TYPE_PLUS";
                    break;
                case MifareClassic.TYPE_PRO:
                    typeS = "TYPE_PRO";
                    break;
                case MifareClassic.TYPE_UNKNOWN:
                    typeS = "TYPE_UNKNOWN";
                    break;
                }
                metaInfo += "\n卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共"
                        + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n";
                for (int j = 0; j < sectorCount; j++) {
                    //Authenticate a sector with key A.
                    auth = mfc.authenticateSectorWithKeyA(j,
                            MifareClassic.KEY_DEFAULT);
                    int bCount;
                    int bIndex;
                    if (auth) {
                        metaInfo += "Sector " + j + ":验证成功\n";
                        // 读取扇区中的块
                        bCount = mfc.getBlockCountInSector(j);
                        bIndex = mfc.sectorToBlock(j);
                        for (int i = 0; i < bCount; i++) {
                            byte[] data = mfc.readBlock(bIndex);
                            metaInfo += "Block " + bIndex + " : "
                                    + bytesToHexString(data) + "\n";
                            bIndex++;
                        }
                    } else {
                        metaInfo += "Sector " + j + ":验证失败\n";
                    }
                }
                promt.setText(metaInfo);
                //Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

4.运行效果图如下:


image.png
上一篇下一篇

猜你喜欢

热点阅读