1. Android NFC开发基础入门NFC近场通信技术如今已经广泛应用于移动支付、门禁系统、公交卡等场景。作为Android开发者掌握NFC开发技能可以让你轻松实现各种创新应用。我刚开始接触NFC开发时最头疼的就是不知道从何入手今天我就把完整的开发流程和踩过的坑都分享给大家。首先需要明确的是NFC通信距离通常在10厘米以内这种短距离特性使其具有较高的安全性。Android系统从API Level 9Android 2.3开始支持基本的NFC功能后续版本不断增强了相关API。在实际开发中我们主要会用到以下几个核心类NfcAdapter相当于设备的NFC功能控制器Tag代表被检测到的NFC标签NdefMessage封装NDEF格式的数据NfcManager管理设备上的所有NFC适配器提示虽然现在大部分Android设备都支持NFC但在开发前还是要做好兼容性检查避免出现功能不可用的情况。2. 权限申请与配置2.1 基础权限声明在AndroidManifest.xml中添加NFC权限是最基础的一步。与摄像头、定位等权限不同NFC权限属于普通权限不需要动态申请uses-permission android:nameandroid.permission.NFC /如果你的应用需要处理特定的NFC标签类型还需要声明对应的功能需求uses-feature android:nameandroid.hardware.nfc android:requiredtrue /这里有个小技巧如果你希望应用能在没有NFC功能的设备上安装只是禁用部分功能可以把required设为false。我在开发一款多功能工具应用时就采用了这种方案。2.2 意图过滤器配置为了让你的Activity能够响应NFC事件需要在清单文件中配置正确的intent-filter。这里最容易出错的就是action的选择我整理了三种常见场景NDEF_DISCOVERED处理包含NDEF数据的标签TECH_DISCOVERED处理特定技术类型的标签TAG_DISCOVERED作为最后兜底的通用处理实际配置示例activity android:name.NfcActivity android:exportedtrue intent-filter action android:nameandroid.nfc.action.NDEF_DISCOVERED/ category android:nameandroid.intent.category.DEFAULT/ data android:mimeTypetext/plain / /intent-filter intent-filter action android:nameandroid.nfc.action.TECH_DISCOVERED/ /intent-filter meta-data android:nameandroid.nfc.action.TECH_DISCOVERED android:resourcexml/nfc_tech_filter / /activity3. NFC功能检测与适配3.1 检查设备支持情况在代码中我们首先要获取NfcAdapter实例并检查设备支持情况NfcAdapter nfcAdapter NfcAdapter.getDefaultAdapter(this); if (nfcAdapter null) { // 设备不支持NFC Toast.makeText(this, 您的设备不支持NFC, Toast.LENGTH_SHORT).show(); return; } if (!nfcAdapter.isEnabled()) { // NFC未开启 Intent enableNfcIntent new Intent(Settings.ACTION_NFC_SETTINGS); startActivity(enableNfcIntent); }这里有个实际开发中的经验有些低端设备虽然返回了NfcAdapter实例但实际NFC功能可能不稳定。我遇到过几台设备会出现间歇性检测不到标签的情况所以建议在关键流程中加入重试机制。3.2 前台调度系统为了让你的Activity在前台时能优先处理NFC事件需要使用enableForegroundDispatch方法。这个机制非常重要我把它比作NFC事件拦截器Override protected void onResume() { super.onResume(); PendingIntent pendingIntent PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); IntentFilter[] intentFilters new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED), new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) }; String[][] techLists new String[][] { { NfcF.class.getName() }, { IsoDep.class.getName() } }; nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, techLists); }记得在onPause中禁用前台调度否则会影响其他应用的NFC功能使用Override protected void onPause() { super.onPause(); nfcAdapter.disableForegroundDispatch(this); }4. NFC标签数据处理4.1 标签检测与基础信息获取当检测到NFC标签时系统会调用Activity的onNewIntent方法。我们可以从这里获取标签信息Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { Tag tag intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (tag ! null) { // 获取标签ID byte[] tagId tag.getId(); String tagIdHex bytesToHex(tagId); // 获取支持的技术列表 String[] techList tag.getTechList(); } } } // 字节数组转十六进制字符串 private static String bytesToHex(byte[] bytes) { StringBuilder sb new StringBuilder(); for (byte b : bytes) { sb.append(String.format(%02x, b)); } return sb.toString(); }4.2 NDEF数据处理对于包含NDEF数据的标签我们可以这样读取内容private void processNdefTag(Tag tag) { Ndef ndef Ndef.get(tag); try { ndef.connect(); NdefMessage ndefMessage ndef.getNdefMessage(); if (ndefMessage ! null) { NdefRecord[] records ndefMessage.getRecords(); for (NdefRecord record : records) { String text parseNdefRecord(record); Log.d(NFC, 读取到NDEF数据: text); } } } catch (Exception e) { e.printStackTrace(); } finally { try { ndef.close(); } catch (Exception e) { e.printStackTrace(); } } } private String parseNdefRecord(NdefRecord record) { // 实际项目中需要根据TNF和类型进行解析 byte[] payload record.getPayload(); return new String(payload, StandardCharsets.UTF_8); }4.3 写入NFC标签写入操作需要特别注意标签的格式化和权限问题private void writeNdefTag(Tag tag, String text) { NdefRecord record NdefRecord.createTextRecord(en, text); NdefMessage message new NdefMessage(new NdefRecord[]{record}); try { Ndef ndef Ndef.get(tag); if (ndef ! null) { ndef.connect(); if (!ndef.isWritable()) { Toast.makeText(this, 标签不可写, Toast.LENGTH_SHORT).show(); return; } int size message.toByteArray().length; if (ndef.getMaxSize() size) { Toast.makeText(this, 标签容量不足, Toast.LENGTH_SHORT).show(); return; } ndef.writeNdefMessage(message); Toast.makeText(this, 写入成功, Toast.LENGTH_SHORT).show(); } else { // 处理未格式化的标签 NdefFormatable formatable NdefFormatable.get(tag); if (formatable ! null) { formatable.connect(); formatable.format(message); Toast.makeText(this, 格式化并写入成功, Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, 写入失败: e.getMessage(), Toast.LENGTH_SHORT).show(); } }5. 高级功能与优化技巧5.1 处理特定类型的NFC标签在实际项目中我们经常需要处理特定类型的NFC标签。比如公交卡通常使用ISO-DEP技术private void processIsoDepTag(Tag tag) { IsoDep isoDep IsoDep.get(tag); try { isoDep.connect(); byte[] command {(byte)0x00, (byte)0xA4, 0x04, 0x00, 0x08, (byte)0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00}; byte[] response isoDep.transceive(command); // 处理响应数据 } catch (IOException e) { e.printStackTrace(); } finally { try { isoDep.close(); } catch (IOException e) { e.printStackTrace(); } } }5.2 性能优化建议连接超时设置对于可能长时间无响应的标签建议设置超时后台处理耗时的NFC操作应该放在后台线程错误恢复实现自动重试机制处理临时性错误电量优化合理控制NFC扫描的激活时间// 设置超时示例 IsoDep isoDep IsoDep.get(tag); try { isoDep.setTimeout(3000); // 3秒超时 isoDep.connect(); // ... } catch (Exception e) { // 处理超时 }5.3 安全注意事项敏感操作需要用户确认验证标签的真实性加密存储的重要数据防止重放攻击我在开发一个NFC支付功能时就遇到了中间人攻击的问题。后来通过添加时间戳和数字签名解决了这个问题private boolean verifyTagSignature(Tag tag, byte[] signature) { // 实际项目中应该使用更安全的验证方式 byte[] tagId tag.getId(); // ...验证逻辑... return true; }6. 实战案例NFC门禁系统下面通过一个简化版的门禁系统案例展示NFC开发的完整流程。这个案例实现了以下功能读取NFC卡ID验证卡的有效性记录刷卡日志提供管理界面6.1 数据结构设计首先定义卡数据和刷卡记录的数据结构public class NfcCard { private String id; private String owner; private Date expireDate; private boolean isActive; // getters setters } public class AccessLog { private String cardId; private Date accessTime; private boolean granted; // getters setters }6.2 核心功能实现主Activity处理NFC事件的核心逻辑public class NfcAccessActivity extends AppCompatActivity { private NfcAdapter nfcAdapter; private DatabaseHelper dbHelper; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_nfc_access); dbHelper new DatabaseHelper(this); nfcAdapter NfcAdapter.getDefaultAdapter(this); } Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { Tag tag intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (tag ! null) { String tagId bytesToHex(tag.getId()); checkAccessPermission(tagId); } } } private void checkAccessPermission(String tagId) { NfcCard card dbHelper.getCardById(tagId); AccessLog log new AccessLog(); log.setCardId(tagId); log.setAccessTime(new Date()); if (card ! null card.isActive() card.getExpireDate().after(new Date())) { log.setGranted(true); runOnUiThread(() - showAccessGranted(card.getOwner())); } else { log.setGranted(false); runOnUiThread(() - showAccessDenied()); } dbHelper.addAccessLog(log); } // ...其他辅助方法... }6.3 数据库帮助类使用Room数据库存储卡数据和访问记录Database(entities {NfcCard.class, AccessLog.class}, version 1) public abstract class AppDatabase extends RoomDatabase { public abstract NfcCardDao cardDao(); public abstract AccessLogDao logDao(); } Dao public interface NfcCardDao { Insert void insert(NfcCard card); Query(SELECT * FROM nfc_card WHERE id :id) NfcCard getById(String id); // 其他查询方法 }7. 常见问题排查在NFC开发过程中我遇到过各种各样的问题这里分享几个典型的排查经验标签无法识别检查手机NFC天线位置通常在后置摄像头附近尝试调整标签与手机的接触角度和距离确认标签类型是否被支持NDEF数据读取失败验证标签是否已格式化检查NDEF记录的类型和格式尝试使用其他NFC读取工具对比测试写入操作报错确认标签是否可写检查存储空间是否足够验证写入的数据格式是否正确性能问题避免在主线程执行耗时操作优化数据解析算法考虑使用缓存机制记得有一次我花了整整一天时间排查为什么NFC标签突然无法读取最后发现是因为测试手机的NFC功能被误关了。所以现在我的调试流程第一步永远是检查NFC开关状态。