Android NFC开发终极指南:从基础到高级实战

9 阅读4分钟

本文将通过5大核心场景、完整代码示例及安全实践,深入讲解如何高效开发Android NFC应用。无论您是实现标签读写还是构建HCE支付系统,这里都有您需要的解决方案。


一、开发环境准备

1. 添加权限

<uses-permission android:name="android.permission.NFC" />
<uses-feature 
    android:name="android.hardware.nfc" 
    android:required="true" />

2. 设备兼容检查

val nfcAdapter = NfcAdapter.getDefaultAdapter(context)
if (nfcAdapter == null) {
    showToast("设备不支持NFC")
} else if (!nfcAdapter.isEnabled) {
    startActivity(Intent(Settings.ACTION_NFC_SETTINGS))
}

二、NFC标签读写实战

完整Activity实现

public class NfcReadWriteActivity extends AppCompatActivity {
    private NfcAdapter mNfcAdapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nfc);
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
    }

    // 前台调度系统
    @Override
    protected void onResume() {
        super.onResume();
        Intent intent = new Intent(this, getClass())
            .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_MUTABLE);
        mNfcAdapter.enableForegroundDispatch(
            this, pendingIntent, null, null);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        processNfcIntent(intent);
    }

    private void processNfcIntent(Intent intent) {
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            readNdefTag(tag);
        }
    }

    // 完整读取实现
    private void readNdefTag(Tag tag) {
        Ndef ndef = Ndef.get(tag);
        try {
            ndef.connect();
            NdefMessage ndefMessage = ndef.getNdefMessage();
            if (ndefMessage != null) {
                for (NdefRecord record : ndefMessage.getRecords()) {
                    parseNdefRecord(record);
                }
            }
            ndef.close();
        } catch (IOException | FormatException e) {
            Log.e("NFC", "读取失败: " + e.getMessage());
        }
    }

    private void parseNdefRecord(NdefRecord record) {
        String tnf = getTnfName(record.getTnf());
        String type = new String(record.getType());
        Log.d("NFC_DEBUG", "TNF: " + tnf + ", Type: " + type);
        
        if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
            if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
                String text = parseTextRecord(record);
                showToast("文本内容: " + text);
            }
        }
    }

    private String parseTextRecord(NdefRecord record) {
        byte[] payload = record.getPayload();
        String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
        int languageLength = payload[0] & 0x3F;
        return new String(payload, languageLength + 1, 
            payload.length - languageLength - 1, textEncoding);
    }
}

写入NDEF数据

public void writeNdefToTag(String text) {
    NdefRecord textRecord = NdefRecord.createTextRecord("en", text);
    NdefMessage message = new NdefMessage(new NdefRecord[]{textRecord});
    
    try {
        if (mTag != null) {
            Ndef ndef = Ndef.get(mTag);
            ndef.connect();
            if (ndef.isWritable()) {
                ndef.writeNdefMessage(message);
                showToast("写入成功");
            }
            ndef.close();
        }
    } catch (Exception e) {
        Log.e("NFC_WRITE", "写入失败: " + e.getClass().getSimpleName());
    }
}

三、Android Beam点对点传输

完整实现方案

class BeamActivity : AppCompatActivity() {
    private val nfcAdapter: NfcAdapter? by lazy {
        NfcAdapter.getDefaultAdapter(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_beam)

        // 创建NDEF消息
        val uriRecord = NdefRecord.createUri("https://www.example.com")
        val appRecord = NdefRecord.createApplicationRecord(packageName)
        val ndefMessage = NdefMessage(arrayOf(uriRecord, appRecord))

        // 设置推送消息
        nfcAdapter?.setNdefPushMessage(ndefMessage, this)
    }

    // 接收处理
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.let {
                val messages = it.map { it as NdefMessage }
                processReceivedMessages(messages)
            }
        }
    }

    private fun processReceivedMessages(messages: List<NdefMessage>) {
        messages.forEach { message ->
            message.records.forEach { record ->
                when {
                    record.toUri() != null -> handleUri(record.toUri())
                    record.toMimeType() == "text/plain" -> handleText(record)
                }
            }
        }
    }
}

四、HCE卡模拟开发

完整HCE服务实现

public class MyHostApduService extends HostApduService {
    private static final byte[] AID_CARD = {
        (byte) 0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
    };
    
    private static final String PIN_CODE = "123456";
    private static final byte[] SW_SUCCESS = { (byte) 0x90, 0x00 };

    @Override
    public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
        if (commandApdu == null) return errorResponse();
        
        // 解析APDU指令
        int cla = commandApdu[0] & 0xFF;
        int ins = commandApdu[1] & 0xFF;
        int p1 = commandApdu[2] & 0xFF;
        int p2 = commandApdu[3] & 0xFF;
        
        if (cla == 0x00 && ins == 0xA4) {
            return handleSelectAid(commandApdu);
        } else if (cla == 0x80 && ins == 0x20) {
            return handleVerifyPin(commandApdu);
        }
        return errorResponse();
    }

    private byte[] handleSelectAid(byte[] apdu) {
        if (apdu.length >= 12 && 
            Arrays.equals(Arrays.copyOfRange(apdu, 5, 11), AID_CARD)) {
            return SW_SUCCESS;
        }
        return errorResponse();
    }

    private byte[] handleVerifyPin(byte[] apdu) {
        int pinLength = apdu[4];
        byte[] receivedPin = Arrays.copyOfRange(apdu, 5, 5 + pinLength);
        String inputPin = new String(receivedPin, StandardCharsets.UTF_8);
        
        return inputPin.equals(PIN_CODE) ? SW_SUCCESS : errorResponse();
    }

    private byte[] errorResponse() {
        return new byte[]{ (byte) 0x6A, 0x82 }; // 文件未找到
    }
}

配置文件hce_config.xml

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/hce_service_desc"
    android:requireDeviceUnlock="false">
    
    <aid-group 
        android:description="@string/aid_group_desc"
        android:category="other">
        <aid-filter 
            android:name="F0010203040506"
            android:description="@string/card_aid"/>
    </aid-group>
</host-apdu-service>

五、智能设备配置

Wi-Fi自动连接配置

public NdefMessage createWifiConfigMessage(String ssid, String password) {
    try {
        String wifiConfig = String.format(
            "WIFI:S:%s;T:WPA2;P:%s;;",
            ssid, URLEncoder.encode(password, "UTF-8"));
        
        byte[] mimeBytes = wifiConfig.getBytes(StandardCharsets.UTF_8);
        NdefRecord wifiRecord = NdefRecord.createMime(
            "application/vnd.wfa.wsc", 
            mimeBytes
        );
        return new NdefMessage(new NdefRecord[]{wifiRecord});
    } catch (Exception e) {
        throw new RuntimeException("生成Wi-Fi配置失败", e);
    }
}

六、安全增强实践

加密NDEF数据

public NdefRecord createEncryptedRecord(String plainText, SecretKey key) 
    throws GeneralSecurityException {
    
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    
    byte[] iv = cipher.getIV();
    byte[] encrypted = cipher.doFinal(plainText.getBytes());
    
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    output.write(iv);
    output.write(encrypted);
    
    return NdefRecord.createMime(
        "application/vnd.secure-data", 
        output.toByteArray()
    );
}

七、性能优化技巧

  1. 快速响应策略
@Override
public void onTagDiscovered(Tag tag) {
    // 使用线程池处理耗时操作
    Executors.newSingleThreadExecutor().execute(() -> {
        processTagInBackground(tag);
    });
}
  1. NDEF缓存优化
private static NdefMessage cachedMessage;

public void prepareNdefMessage() {
    if (cachedMessage == null) {
        cachedMessage = buildLargeNdefMessage();
    }
    mNfcAdapter.setNdefPushMessage(cachedMessage, this);
}

八、常见问题解决方案

问题现象排查步骤
无法检测到标签1. 检查设备NFC天线位置
2. 验证标签是否支持NDEF格式
3. 确保APP拥有前台优先级
HCE服务不响应1. 检查AID配置匹配
2. 验证APDU指令格式
3. 确认服务已设置为默认支付应用
Beam传输中断1. 保持设备背靠背对齐
2. 检查消息大小不超过最大限制(通常896字节)
3. 禁用省电模式

九、调试工具推荐

  1. NFC TagInfo:分析标签技术细节
  2. adb logcat过滤命令
    adb logcat | grep -E "NfcService|Ndef"
    
  3. PC/SC读卡器:验证HCE服务的APDU响应

通过示例和架构设计,开发者可以快速构建生产级NFC应用。建议结合官方文档和实际硬件进行测试,确保最佳兼容性和用户体验。