Android NFC技术实现非接触IC卡读写应用开发实战
本文还有配套的精品资源,点击获取
简介:本项目指导如何使用Android SDK开发NFC应用以读写非接触式IC卡,通常用于门禁、公交卡等场景。介绍NFC技术原理,包括其工作模式和在Android上的API使用。详细说明了如何通过AndroidManifest声明权限、使用BroadcastReceiver监听NFC事件、获取Tag对象以及进行读写操作的代码示例。项目还强调了异常处理和数据安全的重要性,并探讨了高级功能如HCE模拟和NDEF消息处理。
1. NFC技术原理与工作模式
NFC技术简介
NFC(Near Field Communication)是一种无线通信技术,允许设备在短距离内进行信息交换。它是RFID(无线射频识别)技术的一个子集,工作频率为13.56 MHz,传输范围一般限制在10厘米以内。NFC技术主要用于移动设备和近距离通信,因其便携性和易用性,已经成为智能设备中的一种流行技术。
NFC的工作模式
NFC有三种工作模式:
- 读写器模式 :NFC设备模拟读卡器,读取或写入NFC标签或智能卡上的数据。
- 卡模拟模式 :NFC设备模拟一个NFC标签,其他NFC读写器可以读取其中的信息。
- 点对点模式 :两个NFC设备之间建立对等连接,互相交换数据。
这些模式共同为NFC技术的应用提供了灵活性和广泛的可能性。
NFC技术的优势
NFC技术的优势主要表现在:
- 快速交互 :NFC的数据传输速度比蓝牙快,配置也更简单。
- 低功耗 :NFC在激活状态下功耗较低。
- 安全性 :NFC通信具有一定的安全机制,如密钥交换、认证过程等。
- 多种功能 :NFC不仅可以用来传输数据,还可以用于身份认证、电子票务、支付等多种场景。
了解NFC技术的原理和工作模式,为后续章节中对Android NFC API的深入探讨和实际应用打下坚实的基础。
2. Android NFC API使用
2.1 NFC API概述
2.1.1 NFC API核心类介绍
Android的NFC API为开发者提供了丰富的工具来实现NFC功能。核心的类包括NfcAdapter,NdefMessage,NdefRecord等,它们分别承担着NFC硬件适配器,NDEF消息,NDEF记录的作用。通过这些类,开发者能够控制NFC硬件,构建和解析NDEF消息,以及读写NFC标签。这些类的使用和它们之间的关联是实现NFC功能的基础。
2.1.2 NFC API的主要方法
NfcAdapter类提供了许多主要方法,例如:enableReaderMode(),disableReaderMode(),和技术isNdefPushEnabled()等,这些方法用来控制NFC适配器,和读写NFC标签。enableReaderMode()方法允许开发者在自定义的模式下使用NFC适配器,这种方式比默认的模式提供了更多的控制选项,例如可以避免系统生成的NDEF消息,从而提高应用的性能。
2.2 NFC标签的读写
2.2.1 NFC标签识别流程
当NFC适配器检测到NFC标签时,会发起一个Intent,该Intent携带标签的信息。通过分析这个Intent,应用可以获取标签的类型(如NDEF,Mifare等)。读取标签数据的过程包括:初始化NfcAdapter,注册相应的Intent Filter以监听NFC事件,然后在onNewIntent()方法中处理接收到的Intent。重要的是,代码逻辑需要能够根据不同的标签类型,调用相应的方法来读取或写入数据。
val intent = intentval action = intent.actionif (NfcAdapter.ACTION_NDEF_DISCOVERED == action) { val ndef: Ndef = Ndef.get(tag) // 确定标签是否支持NDEF格式 // ...}
在上面的代码示例中,首先检查Intent的动作,如果动作是NfcAdapter.ACTION_NDEF_DISCOVERED,表示检测到NDEF格式的标签,然后获取Ndef对象,用于进一步处理标签数据。
2.2.2 NFC标签数据读取与写入
读取NFC标签的数据通常涉及解析NdefMessage,它包含了NDEF记录(NdefRecord)的列表。每个NdefRecord包含了实际的数据,例如文本、URL或其他类型的数据。对于写入,可以创建一个新的NdefMessage,添加NdefRecords,然后写入NFC标签。
NdefRecord[] records = { myRecord };NdefMessage message = new NdefMessage(records);NdefTag tag = Ndef.get(tag);if (tag != null) { NdefFormatable format = NdefFormatable.get(tag); if (format != null) { try { format.connect(); format.format(message); format.close(); } catch (IOException | FormatException e) { e.printStackTrace(); } } else { // 处理无法格式化的情况 }}
在上述代码段中,首先创建了一个包含NdefRecord的数组,然后用它来创建NdefMessage对象。通过NdefFormatable类的实例,调用format()方法将NdefMessage写入NFC标签。
2.3 NFC设备状态监听
2.3.1 检测NFC设备可用性
应用程序应当能够检测到设备是否支持NFC功能,以及NFC功能是否启用。可以通过调用NfcAdapter.getDefaultAdapter()方法来获取NfcAdapter实例,然后通过检查其返回值是否为null来判断设备是否支持NFC。如果返回值不是null,则表明该设备支持NFC。进一步,使用isEnabled()方法来判断NFC功能是否已经启用。
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(context);if (nfcAdapter == null) { // 设备不支持NFC} else if (!nfcAdapter.isEnabled()) { // NFC未启用,可能需要提示用户开启}
2.3.2 监听NFC设备状态变化
为了监听NFC设备状态的变化,需要在相应的Activity中重写onResume()和onPause()方法,并且在这些方法中调用enableReaderMode()和disableReaderMode()。同时,需要实现NfcAdapter.ReaderCallback接口,以获取NFC标签接近或离开时的回调事件。当状态变化时,可以通过回调函数处理相关逻辑。
@Overridepublic void onResume() { super.onResume(); NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter != null) { nfcAdapter.enableReaderMode(this, new NfcAdapter.ReaderCallback() { @Override public void onTagDiscovered(Tag tag) { // 处理检测到的NFC标签 } }, NfcAdapter.FLAG_READER_NFC_A, null); }}@Overridepublic void onPause() { super.onPause(); NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter != null) { nfcAdapter.disableReaderMode(this); }}
在这段代码中,通过enableReaderMode()方法注册ReaderCallback,当检测到NFC标签时,会回调onTagDiscovered()方法。
3. AndroidManifest权限声明
在Android开发中,合理的权限声明是确保应用安全性和功能正常运行的基础。本章深入探讨AndroidManifest文件中与NFC相关的权限配置,以及一些必要的其他权限声明。
3.1 NFC权限配置
NFC技术的使用需要在AndroidManifest.xml文件中声明NFC权限,同时根据Android版本的不同,权限的请求方式和细节也会有所变化。
3.1.1 Android 6.0及以上权限动态请求
从Android 6.0(API级别23)开始,对敏感权限需要动态请求。NFC属于敏感权限之一,因此开发者需要在代码中实现动态请求逻辑。
// 动态请求NFC权限if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.NFC) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.NFC}, MY_PERMISSIONS_REQUEST_NFC);}
上述代码段中, checkSelfPermission 用于检查应用是否有NFC权限,如果没有则通过 requestPermissions 弹出对话框请求用户授权。
3.1.2 NFC权限与其他权限的关联
在某些情况下,NFC权限的请求可能与其他权限相关联。例如,如果应用需要在NFC标签中存储和读取数据,则可能还需要 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。
3.2 其他必要权限声明
除了NFC权限外,某些功能的实现还需要额外的权限声明。
3.2.1 网络通信权限
在涉及到网络通信时,如需要通过NFC进行数据交换并上传到服务器,还需要声明网络通信权限。
3.2.2 存储权限
由于NFC标签的数据可能需要存储到设备的内部存储中,因此需要声明存储权限。
3.2.3 电话权限
当NFC用于呼叫或发送短信时,还需要获取电话权限。
3.2.4 位置权限
如果应用依赖于地理位置信息进行某些NFC相关的操作,那么位置权限是必不可少的。
通过上述章节的深入探讨,我们理解了在Android应用中声明NFC权限及其与其他权限之间的关联性。开发者必须根据应用的具体需求合理配置权限,确保应用在不同版本的Android系统中都能正常运行,同时遵守平台的权限管理规则,以保护用户的隐私安全。在接下来的章节中,我们将继续深入NFC技术的其他方面,并探讨如何处理NFC事件,以及如何通过权限声明来确保NFC操作的正常执行。
4. BroadcastReceiver与NFC事件处理
4.1 BroadcastReceiver基础
4.1.1 BroadcastReceiver角色和功能
BroadcastReceiver是Android系统用于接收应用程序发送或系统发送的广播消息的组件。在NFC应用开发中,BroadcastReceiver扮演着监听NFC事件的角色,当检测到NFC标签时,系统会发送一个动作给注册了相应Intent Filter的BroadcastReceiver。因此,BroadcastReceiver能够及时响应系统广播消息并作出相应的处理。
4.1.2 创建和注册BroadcastReceiver
在Android应用中创建BroadcastReceiver需要继承BroadcastReceiver类,并重写onReceive方法,在该方法中处理接收到的广播。注册BroadcastReceiver可以通过两种方式完成,一种是在AndroidManifest.xml中声明,另一种是在代码中动态注册。
// 在AndroidManifest.xml中注册BroadcastReceiver示例 // 在代码中动态注册BroadcastReceiver示例BroadcastReceiver nfcReceiver = new NFCReceiver();IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);registerReceiver(nfcReceiver, filter);
当NFC标签被检测到时,系统会调用NFCReceiver中的onReceive方法。因此,开发人员需要在该方法中实现NFC事件处理逻辑。
4.2 NFC事件处理机制
4.2.1 Intent Filter配置
Intent Filter用于告知系统该BroadcastReceiver可以接收哪些Intent。在NFC应用中,需要配置相应的Intent Filter以监听NFC事件,如NDEF_DISCOVERED,和技术特定的MIME类型。
4.2.2 NFC事件处理流程详解
当NFC标签进入NFC适配器的感应区域时,系统会触发NFC事件,并通过Intent发送给所有的BroadcastReceiver。onReceive方法被调用,应用有机会读取标签信息。接下来,应用可以解析Intent中的NDEF消息,并执行相应的操作。
public class NFCReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); // 处理NDEF消息 } }}
4.3 NFC事件优化策略
4.3.1 减少前台服务的使用
在处理NFC事件时,尽量避免使用前台服务,因为它们会消耗系统资源,并且可能对用户体验产生负面影响。合理使用前台通知来引导用户在需要时打开应用即可。
4.3.2 NFC事件的快速处理技巧
为了优化NFC事件处理,应尽量减少onReceive方法中的计算和网络操作。可以使用异步任务或服务来处理这些操作,同时确保应用响应迅速,提供流畅的用户体验。
在下一章节中,我们将详细探讨如何通过AndroidManifest配置NFC相关的权限,并分享实现非接触IC卡读写操作的最佳实践。
5. 非接触IC卡读写操作实现
5.1 IC卡技术概述
5.1.1 IC卡的分类与特性
IC卡(Integrated Circuit Card),也被称为智能卡,是含有微处理器和存储器的卡片,与NFC技术结合后,广泛用于身份验证、电子支付和门禁系统等地方。IC卡根据其技术特性主要分为两类:
- 存储卡(Memory Card) :这类IC卡内部仅有存储器,不包含微处理器,其功能相对简单,主要用于存储数据。其安全性较低,通常不涉及复杂的加密操作。
- 智能卡(Smart Card) :这类IC卡内部集成有微处理器(CPU),能够执行程序和复杂的加密算法,通常具备较高安全性。智能卡分为接触式(需要插槽读取)和非接触式(NFC技术读取)两种。
5.1.2 IC卡与NFC技术的结合
NFC技术使得非接触IC卡的应用更加广泛,用户只需靠近NFC读写器或支持NFC的设备即可完成交易或认证。非接触IC卡相对于传统接触式IC卡具有以下几个优点:
- 快速交互 :无需物理插拔,能够实现快速的交易处理。
- 便捷性 :用户不需要精确对准卡片位置,方便快捷。
- 耐用性 :由于没有机械接触,因此磨损和损坏的可能性较低。
5.2 IC卡读写实践
5.2.1 IC卡数据结构分析
IC卡的数据结构通常包括卡序列号、用户数据区域和应用数据区域等。在进行读写操作前,了解IC卡的数据结构是至关重要的:
- 卡序列号 :每张卡片的唯一标识,用于识别卡片。
- 用户数据区域 :存储用户信息,如个人信息、余额等。
- 应用数据区域 :根据不同的应用需求,可以有多个应用数据区域,用于存储特定应用的数据。
5.2.2 实现IC卡数据读取
要从IC卡读取数据,首先需要检测到卡片并建立通信。以下是一个简单的示例代码,展示如何使用Android NFC API来读取IC卡的数据:
// 示例代码:读取IC卡数据public void readICCardData(NdefMessage ndefMessage) { NdefRecord[] records = ndefMessage.getRecords(); for (NdefRecord record : records) { if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) { try { // 将文本记录的数据转换为字符串 String text = readText(record); // 输出读取到的数据 Log.d(\"ICCard\", \"Read data: \" + text); } catch (UnsupportedEncodingException e) { Log.e(\"ICCard\", \"Unsupported Encoding\", e); } } }}private String readText(NdefRecord record) throws UnsupportedEncodingException { // 获取文本编码方式 String languageCode = new String(record.getLanguageCode()); // 获取文本数据 byte[] textBytes = record.getPayload(); // 根据编码方式解码文本数据 String text = new String(textBytes, getCharset(languageCode)); return text;}private Charset getCharset(String languageCode) { // 针对特定语言编码设置相应的字符集 if (Arrays.asList(\"en\", \"fr\", \"de\").contains(languageCode)) { return StandardCharsets.UTF_8; } else { return StandardCharsets.ISO_8859_1; }}
在上述代码中,我们首先检查了NDEF消息中的记录类型。如果记录类型为文本记录( TNF_WELL_KNOWN && RTD_TEXT ),我们则尝试解析其内容。其中, readText 方法通过记录中指定的语言编码来解码文本信息。
5.2.3 实现IC卡数据写入
写入IC卡数据需要使用到 NdefMessage 和 NdefRecord 类。以下是一个简单的示例,展示如何构建一个NDEF消息并写入数据到IC卡:
// 示例代码:写入数据到IC卡public void writeICCardData(String data) { // 创建一个NDEF文本记录 NdefRecord record = createTextRecord(\"en\", data); // 创建NDEF消息 NdefMessage message = new NdefMessage(new NdefRecord[]{record}); // 准备写入操作 prepareWriteTag(message);}private NdefRecord createTextRecord(String languageCode, String text) throws UnsupportedEncodingException { // 创建文本记录 byte[] language = languageCode.getBytes(StandardCharsets.ISO_8859_1); byte[] textBytes = text.getBytes(StandardCharsets.UTF_8); // 返回NDEF文本记录 return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, language, textBytes);}private void prepareWriteTag(NdefMessage message) { // 获取NFC适配器 NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null) { Toast.makeText(this, \"NFC is not available on this device.\", Toast.LENGTH_LONG).show(); return; } // 开启前台调度系统 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); // 设置前台调度选项 NdefFormatable ndefFormatable = NdefFormatable.get(this); if (ndefFormatable != null) { // 尝试格式化NFC标签 try { ndefFormatable.connect(); ndefFormatable.format(message); } catch (IOException e) { Log.e(\"ICCard\", \"Failed to format tag.\", e); } } else { // 创建NFC技术适配器选项 NfcTechnology nfcTechnology = new NfcTechnology(nfcAdapter, this, getLifecycle()); // 启动NFC技术适配器 nfcTechnology.enable(); }}
在这段代码中, createTextRecord 方法用于创建一个NDEF文本记录, prepareWriteTag 方法则是启动一个前台调度系统并尝试写入NDEF消息到IC卡。如果NFC标签支持格式化, ndefFormatable.format(message) 将被调用来格式化标签并写入数据。需要注意的是,通常需要有相应的权限和检测条件才能执行写入操作。
在处理IC卡数据时,开发者需要仔细考虑不同IC卡的协议和特性,以及如何合理地处理可能出现的异常和错误。此外,数据安全也是不容忽视的问题,涉及到数据的加密和认证操作。在实际应用中,开发者还需要遵循行业标准和法规要求来确保数据传输的安全。
6. Mifare Classic卡片数据读写示例代码
6.1 Mifare Classic技术特点
6.1.1 Mifare Classic卡片结构
Mifare Classic是一种广泛使用的非接触式智能卡技术,属于ISO/IEC 14443标准。卡片内部主要由三个部分组成:非易失性存储器、安全逻辑单元、射频接口单元。非易失性存储器又分为16个扇区,每个扇区包含4个数据块,每个扇区的第一块用于存储密钥和访问控制数据。卡片通常有三种类型:1K、4K和UltraLight,容量从小到大。
6.1.2 安全性能分析
Mifare Classic的安全性基于3DES加密算法。每个扇区都有独立的密钥(A和B),用于读取和写入操作。为了保护数据,Mifare Classic提供了多种访问条件。然而,由于固有的安全缺陷,如算法和密钥长度限制,Mifare Classic已被证明可以被攻击破解,因此在安全要求极高的应用场景下,建议使用更为安全的卡片技术。
6.2 Mifare Classic编程实践
6.2.1 密钥管理与认证过程
在对Mifare Classic进行操作前,必须进行密钥管理。以下是密钥管理与认证过程的关键步骤:
- 获取卡片实例并初始化。
- 发送认证命令以对特定扇区进行认证。
- 如需读写操作,选择相应的密钥进行加密认证。
下面是一个简单的Java代码示例,演示如何与Mifare Classic卡片进行通信:
// 假设已经获取到NfcA类型的tag实例NdefA ndefA = tag.getNdefA();if (ndefA != null) { // 选择卡片 ndefA.connect(); // 选择扇区 ndefA.select(0x04); // 选择密钥A进行认证 byte[] keyA = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; boolean isAuthenticated = ndefA.authenticateSectorWithKeyA(0, keyA); if (isAuthenticated) { // 执行读写操作 // ... } // 断开与卡片的连接 ndefA.close();}
6.2.2 数据块的读写示例
在成功认证后,您可以进行数据块的读写操作。数据块读取使用 readSector 方法,写入则使用 writeSector 方法。以下为相应的代码示例:
// 读取数据块,这里假设读取第一个扇区的第三个块byte[] data = ndefA.readSector(0, 2);// 写入数据块,这里假设向第一个扇区的第三个块写入数据byte[] newData = {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF};ndefA.writeSector(0, 2, newData);
重要的是要注意,对于Mifare Classic卡片的读写操作,在执行前需要考虑卡片是否已经认证,同时也要确保在实际操作中遵循安全实践,比如使用加密的连接,以及不公开密钥等重要信息。
数据的读取和写入应该被妥善管理,以确保数据的安全性和完整性。在处理数据时,应该进行适当的异常处理,以避免运行时错误导致数据损坏或数据泄露。
在下一章节,我们将讨论如何处理在进行NFC操作时可能遇到的异常,以及如何在NFC通信中保护数据安全。
7. 异常处理与数据安全
7.1 异常处理机制
NFC开发中难免会遇到各种异常情况,例如设备不支持、权限被拒绝、读写错误等。理解这些异常,并采取正确的处理策略是保证应用稳定运行的重要部分。
7.1.1 NFC常见异常及其应对策略
java.lang.RuntimeException: Near field communication (NFC) is not available. java.lang.IllegalStateException: NFC is not enabled on this device. android.nfc.TagLostException 代码示例:
try { // NFC Tag交互代码} catch (TagLostException e) { // 捕获到Tag丢失异常 Log.d(TAG, \"NFC Tag lost.\"); // 通知用户重新进行操作或进行重试}
7.1.2 异常处理的最佳实践
- 日志记录 :记录异常信息对于调试和维护应用非常重要。使用日志框架进行详细的异常记录。
- 用户友好的提示 :遇到异常时,应给用户提供清晰的提示,而不是仅仅显示程序错误。
- 资源释放 :确保在异常情况下正确释放资源,如关闭输入/输出流等。
- 异常捕获的最小范围 :只在必要的代码块中捕获异常,以避免遮蔽其他潜在的错误。
7.2 数据安全保护
NFC数据交换在带来便利的同时,也带来了数据安全的风险。保护数据安全不仅涉及数据传输过程的加密,还包括在存储和使用过程中的安全措施。
7.2.1 NFC数据加密与解密
使用加密技术保护NFC传输的数据是防止数据被截获或篡改的有效手段。常见的加密技术有:
- 对称加密 :使用同一个密钥进行数据的加密和解密,如AES。
- 非对称加密 :使用一对公钥和私钥,公钥加密的数据只能用对应的私钥解密,如RSA。
7.2.2 NFC通信安全的实现
- NFC数据传输加密 :采用NFC标准中的加密协议,比如NFC的安全传输协议NFC-SEC。
- 数据签名验证 :通信双方通过数字签名进行身份验证和数据完整性校验,防止数据被伪造或篡改。
- 使用HCE限制访问权限 :通过HCE(Host-based Card Emulation)进行访问控制,提高数据安全性。
- 传输过程中的密钥管理 :确保传输过程中使用的是安全密钥,并定期更新。
代码示例:
// 假设使用AES加密算法KeyGenerator keyGenerator = KeyGenerator.getInstance(\"AES\");keyGenerator.init(256); // 设置密钥长度为256位SecretKey secretKey = keyGenerator.generateKey();byte[] keyBytes = secretKey.getEncoded();// 将密钥保存到安全存储中或进行传输
上述代码展示了如何生成一个AES密钥,实际应用中需要将密钥安全地保存在设备上或安全地传输给通信双方。
7.2.3 NFC数据存储安全
- 加密存储 :在设备本地存储NFC数据时,应对其进行加密。
- 访问权限控制 :设置文件和数据的访问权限,防止未授权的读取或修改。
在Android系统中,可以利用加密的SharedPreferences来安全存储敏感信息。例如:
// 使用SharedPreferences存储加密的数据SharedPreferences securePrefs = getSharedPreferences(\"SecurePrefs\", MODE_PRIVATE);SharedPreferences.Editor editor = securePrefs.edit();// 加密数据String encryptedValue = encryptData(\"Secret data\");editor.putString(\"secret\", encryptedValue);// 提交更改editor.apply();
这里 encryptData 方法是一个假设的加密函数,需要使用一个安全的加密库进行实现。这样,即使数据被读取,未授权的用户也无法理解数据内容。
本文还有配套的精品资源,点击获取
简介:本项目指导如何使用Android SDK开发NFC应用以读写非接触式IC卡,通常用于门禁、公交卡等场景。介绍NFC技术原理,包括其工作模式和在Android上的API使用。详细说明了如何通过AndroidManifest声明权限、使用BroadcastReceiver监听NFC事件、获取Tag对象以及进行读写操作的代码示例。项目还强调了异常处理和数据安全的重要性,并探讨了高级功能如HCE模拟和NDEF消息处理。
本文还有配套的精品资源,点击获取


