Android NFC 技术详解及 IC 卡读取实现

发布于:2025-07-22 ⋅ 阅读:(18) ⋅ 点赞:(0)

NFC(Near Field Communication,近场通信)作为一种短距离高频无线通信技术,在移动支付、身份识别、数据传输等场景中应用广泛。在 Android 设备上,NFC 功能可以实现与 IC 卡、标签、其他 NFC 设备的交互,其中 “读取 IC 卡” 是最常见的需求之一。本文将从技术原理到实际开发,全面讲解 Android NFC 技术及 IC 卡读取实现。

一、Android NFC 技术基础

1.1 什么是 NFC?

NFC 是一种基于 RFID(射频识别)技术的短距离通信标准,工作频率为 13.56MHz,通信距离通常在 10cm 以内。相比蓝牙、WiFi 等无线技术,NFC 具有无需配对、响应速度快、功耗低的特点,非常适合 “接触式” 交互场景(如刷公交卡、门禁卡)。

NFC 技术兼容三种主要标准:

  • ISO 14443:非接触式 IC 卡标准(如常见的 Mifare 卡、CPU 卡)
  • ISO 15693:近场通信标签标准(如电子标签)
  • ISO 18092:NFC 设备间点对点通信标准(如手机传文件)

1.2 Android 中的 NFC 工作模式

Android 系统从 API 9(Android 2.3)开始支持 NFC 功能,核心工作模式分为三种:

  • 读卡器模式(Reader/Writer Mode):Android 设备作为主动方,读取 NFC 标签或 IC 卡中的数据(本文重点讲解此模式)。
  • 卡模拟模式(Card Emulation Mode):Android 设备模拟成一张 NFC 卡(如手机刷公交时,手机模拟成交通卡)。
  • 点对点模式(P2P Mode):两台 NFC 设备直接交换数据(如手机间传照片)。

1.3 Android NFC 核心组件

在 Android 开发中,NFC 相关功能主要通过以下核心类实现:

  • NfcAdapter:设备 NFC 功能的入口,用于管理 NFC 适配器(需先判断设备是否支持 NFC)。
  • Tag:表示被检测到的 NFC 标签或 IC 卡,包含卡片的类型、ID、协议等信息。
  • TechList:标签支持的技术列表(如 MifareClassic、IsoDep 等),不同 IC 卡对应不同的技术实现。
  • Intent:NFC 标签检测的触发机制,通过系统广播(如ACTION_NDEF_DISCOVERED、ACTION_TECH_DISCOVERED)通知应用处理标签。

二、IC 卡与 NFC 的关联

IC 卡(集成电路卡)是通过芯片存储数据的卡片,按通信方式可分为接触式(如银行卡)和非接触式(如公交卡)。非接触式 IC 卡正是通过 NFC 技术实现数据交互的,因此 “读取 IC 卡” 本质上是 Android 设备在 “读卡器模式” 下与 IC 卡建立通信并获取数据。

2.1 常见 IC 卡类型及 NFC 兼容性

不同 IC 卡遵循的协议不同,对应的 Android NFC 技术支持也不同,常见类型如下:

IC 卡类型

协议标准

适用场景

Android 支持情况

Mifare Classic

ISO 14443A

校园卡、门禁卡

需设备支持,Android 10 + 限制部分功能

Mifare Ultralight

ISO 14443A

电子标签、门票

大部分设备支持,数据读取无特殊限制

CPU 卡

ISO 14443A/B

身份证、金融 IC 卡

需通过加密认证,支持 ISO_DEP 技术

Felica

ISO 18092

日本交通卡(Suica)

仅部分设备支持(如日系机型)

其中,Mifare Classic(MF1 卡) 是最常见的民用 IC 卡(如小区门禁、校园一卡通),也是开发中最常接触的类型,但需注意:Android 10(API 29)及以上系统对 Mifare Classic 的读取做了限制(需设备厂商授权),部分设备可能无法读取。

三、Android 读取 IC 卡的开发步骤

读取 IC 卡的核心逻辑是:当 IC 卡靠近 Android 设备时,系统通过 NFC 检测到标签,应用接收标签数据并解析。以下是完整开发流程。

3.1 开发前准备

  • 硬件要求:Android 设备需支持 NFC(可通过PackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)判断)。
  • 系统版本:最低支持 Android 2.3(API 9),但建议基于 Android 4.0(API 14)及以上开发(支持更多标签类型)。
  • 权限配置:需在AndroidManifest.xml中声明 NFC 权限。

3.2 配置清单文件(AndroidManifest.xml)

清单文件需完成三件事:声明权限、配置 NFC 过滤规则、指定启动模式(避免重复创建 Activity)。

<!-- 声明NFC权限 -->
<uses-permission android:name="android.permission.NFC" />

<!-- 声明设备需支持NFC(可选,用于Google Play过滤) -->
<uses-feature
    android:name="android.hardware.nfc"
    android:required="true" />

<application ...>
    <activity
        android:name=".ICCardReaderActivity"
        android:launchMode="singleTop">  <!-- 单顶模式,避免重复创建 -->
        
        <!-- NFC标签过滤规则:指定可处理的标签类型 -->
        <intent-filter>
            <action android:name="android.nfc.action.TECH_DISCOVERED" />
        </intent-filter>
        
        <!-- 支持的标签技术列表(根据IC卡类型配置) -->
        <meta-data
            android:name="android.nfc.action.TECH_DISCOVERED"
            android:resource="@xml/nfc_tech_filter" />
    </activity>
</application>

其中nfc_tech_filter.xml(位于res/xml目录)用于指定支持的标签技术,例如支持 Mifare Classic 和 ISO 14443A:

<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>  <!-- ISO 14443A类型 -->
    </tech-list>
</resources>

3.3 初始化 NFC 适配器

在 Activity 中,首先需要初始化NfcAdapter,并判断设备是否支持 NFC:

public class ICCardReaderActivity extends AppCompatActivity {
    private NfcAdapter mNfcAdapter;
    private PendingIntent mPendingIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_iccard_reader);

        // 初始化NFC适配器
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mNfcAdapter == null) {
            Toast.makeText(this, "设备不支持NFC", Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        // 检查NFC是否开启
        if (!mNfcAdapter.isEnabled()) {
            Toast.makeText(this, "请开启NFC功能", Toast.LENGTH_SHORT).show();
            // 可跳转到NFC设置页
            startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
        }

        // 创建PendingIntent:当检测到标签时,系统通过此Intent启动当前Activity
        mPendingIntent = PendingIntent.getActivity(
            this, 0,
            new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
            PendingIntent.FLAG_IMMUTABLE
        );
    }
}

3.4 处理 NFC 标签数据

当 IC 卡靠近设备时,系统会通过PendingIntent触发 Activity 的onNewIntent方法,我们需要在此方法中获取标签数据并解析。

核心步骤:

1.从 Intent 中获取Tag对象(标签实例);

2.根据标签支持的技术(如MifareClassic)创建对应的数据读取对象;

3.连接标签并读取数据;

4.解析数据(需根据 IC 卡数据格式处理)。

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    // 判断是否为NFC标签 Intent
    if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
        // 获取标签对象
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (tag != null) {
            readICCardData(tag); // 读取IC卡数据
        }
    }
}

// 读取IC卡数据(以Mifare Classic为例)
private void readICCardData(Tag tag) {
    // 获取Mifare Classic标签实例
    MifareClassic mifare = MifareClassic.get(tag);
    if (mifare == null) {
        Toast.makeText(this, "不支持该类型IC卡", Toast.LENGTH_SHORT).show();
        return;
    }

    try {
        // 连接标签
        mifare.connect();
        // 获取卡片类型(如Mifare Classic 1K/4K)
        String type = mifare.getType() == MifareClassic.TYPE_CLASSIC ? "Classic" :
                      mifare.getType() == MifareClassic.TYPE_PLUS ? "Plus" : "UltraLight";
        // 获取扇区数和块数
        int sectorCount = mifare.getSectorCount();
        int blockCount = mifare.getBlockCount();

        // 读取数据(以读取第0扇区第0块为例,需注意:扇区0的块0通常存储厂商信息,不可修改)
        // 注意:读取扇区需先验证密钥(默认密钥可能为0xFFFFFFFFFFFF)
        boolean auth = mifare.authenticateSectorWithKeyA(0, MifareClassic.KEY_DEFAULT);
        if (auth) {
            // 获取扇区内的块索引
            int blockIndex = mifare.sectorToBlock(0);
            // 读取块数据(16字节)
            byte[] data = mifare.readBlock(blockIndex);
            // 解析数据(根据实际格式转换,这里转为16进制字符串)
            String hexData = bytesToHex(data);
            Log.d("ICCard", "扇区0块0数据:" + hexData);
            Toast.makeText(this, "读取成功:" + hexData, Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, "密钥验证失败,无法读取数据", Toast.LENGTH_SHORT).show();
        }
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "读取失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    } finally {
        try {
            // 关闭连接
            mifare.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// 字节数组转16进制字符串(辅助方法)
private String bytesToHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        String hex = Integer.toHexString(b & 0xFF);
        if (hex.length() == 1) sb.append("0");
        sb.append(hex).append(" ");
    }
    return sb.toString();
}

3.5 生命周期管理

为确保在 Activity 处于不同状态时(如前台、后台)都能接收 NFC 事件,需在onResume和onPause中开启 / 关闭 NFC 前台调度:

@Override
protected void onResume() {
    super.onResume();
    if (mNfcAdapter != null) {
        // 开启前台调度:优先接收NFC事件
        mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
    }
}

@Override
protected void onPause() {
    super.onPause();
    if (mNfcAdapter != null) {
        // 关闭前台调度
        mNfcAdapter.disableForegroundDispatch(this);
    }
}

四、常见问题及解决方案

4.1 读取 Mifare Classic 卡失败(Android 10+)

Android 10(API 29)及以上系统对MifareClassic类做了限制:非系统应用无法直接访问 Mifare Classic 卡的完整功能(如密钥验证、数据读写),会抛出SecurityException。

解决方案

  • 若设备已 Root,可尝试通过系统权限绕过限制;
  • 优先使用NfcA或IsoDep等通用技术类读取(部分数据可兼容);
  • 针对特定设备,可联系厂商获取 Mifare Classic 访问授权。

4.2 密钥验证失败

IC 卡的扇区通常有密钥保护(Key A 和 Key B),默认密钥(如0xFFFFFFFFFFFF)仅适用于未加密的卡片。若卡片已被自定义加密(如校园卡、企业门禁卡),需获取对应密钥才能读取。

提示:部分卡片的公共扇区(如厂商信息区)可能使用默认密钥,可优先尝试读取这些区域。

4.3 标签检测不稳定

  • 确保 IC 卡与设备 NFC 天线位置对齐(通常在手机背部上方);
  • 避免金属遮挡(金属会屏蔽 NFC 信号);
  • 检查设备 NFC 功能是否正常(可通过读取其他标签测试)。

五、总结

Android NFC 读取 IC 卡的核心流程可概括为:配置权限与过滤规则→初始化 NFC 适配器→监听标签事件→解析标签数据。实际开发中需注意不同 IC 卡的协议差异(如 Mifare、ISO 14443)和系统限制(如 Android 10 + 的 Mifare 限制)。

若需读取加密卡数据,需提前获取密钥或通过合法渠道获取卡片数据格式;对于普通应用,可优先实现对公开数据(如卡片 ID、厂商信息)的读取,满足基础需求。

通过本文的步骤,你可以快速搭建一个基础的 IC 卡读取应用,后续可根据实际场景扩展功能(如数据解析、历史记录存储等)。


网站公告

今日签到

点亮在社区的每一天
去签到