本文将通过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()
);
}
七、性能优化技巧
- 快速响应策略
@Override
public void onTagDiscovered(Tag tag) {
// 使用线程池处理耗时操作
Executors.newSingleThreadExecutor().execute(() -> {
processTagInBackground(tag);
});
}
- 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. 禁用省电模式 |
九、调试工具推荐
- NFC TagInfo:分析标签技术细节
- adb logcat过滤命令:
adb logcat | grep -E "NfcService|Ndef"
- PC/SC读卡器:验证HCE服务的APDU响应
通过示例和架构设计,开发者可以快速构建生产级NFC应用。建议结合官方文档和实际硬件进行测试,确保最佳兼容性和用户体验。