Android中如何让AlertDialog即时显示NFC卡片挑战码?
问题原因
你的onNewIntent方法运行在主线程中,虽然你用handler.post()触发AlertDialog显示,但后续的API.cardAuthenticate()等NFC认证操作同样在主线程执行,这些耗时操作会阻塞UI线程,导致Dialog渲染被延迟,直到所有主线程任务完成后才会显示。
解决方案
要实现Dialog即时显示并等待用户验证,需要把耗时的NFC认证逻辑移到后台线程,同时让Dialog的交互触发后续流程:
1. 移除主线程阻塞的StrictMode设置
首先删掉onCreate里的StrictMode代码,它允许主线程执行耗时操作,是UI阻塞的根源之一:
// 移除这段代码 if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); }
2. 用后台线程处理NFC通信,主线程显示Dialog
通过Thread+runOnUiThread分离UI与耗时操作,确保Dialog即时显示并等待用户确认后再执行认证:
@RequiresApi(api = Build.VERSION_CODES.O) @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); System.out.println("Doing onNewIntent() ..."); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); boolean hasIsoDep = false; if (tag != null) { System.out.println("New tag found !!!"); System.out.println("Tag ID: " + BinUtils.toHexString(tag.getId())); for (String tagTech : tag.getTechList()) { System.out.println("Tech: " + tagTech); if (tagTech.equals("android.nfc.tech.IsoDep")) { hasIsoDep = true; } } } else { System.out.println("No new tag found !!!"); } if (hasIsoDep) { // 耗时NFC操作放到后台线程 new Thread(() -> { IsoDep iso14443 = null; try { iso14443 = IsoDep.get(tag); iso14443.connect(); SharedResource.setIsoDep(iso14443); System.out.println("Selecting KeyVault applet ..."); HashMap<String, Object> result = API.adminInit(); boolean isSuccess = (boolean) result.get("Success"); if (isSuccess) { String toShow = (String) result.get("Message"); if (toShow != null) { System.out.println("Showing Secure Session popup ..."); // 主线程显示Dialog,等待用户确认 runOnUiThread(() -> { AlertDialog.Builder builder = new AlertDialog.Builder(CardPairingActivity.this); builder.setTitle("Secure Card Session"); builder.setMessage(toShow); builder.setPositiveButton("确认", (dialog, which) -> { // 用户确认后,后台继续执行认证 new Thread(() -> { try { if (API.cardAuthenticate(Constants.USER_KEYMAN, username, password.toCharArray())) { System.out.println("[INF] Card Login SUCCESS ..."); // 登录成功的UI操作回到主线程 runOnUiThread(() -> { // 示例:跳转至成功页面 }); } else { System.out.println("[ERR] Card Login FAILED ..."); int tries = API.adminCardLoginTriesRemaining(role, null); runOnUiThread(() -> { if (tries != -1) { SharedResource.showNotificationDialog("Card Pairing Failed", "Card Login Failed. \r\nLogin Retries Left: " + tries, "OK", CardPairingActivity.this, MainActivity.class); } else { SharedResource.showNotificationDialog("Card Pairing Failed", "Card Login Failed.", "OK", CardPairingActivity.this, MainActivity.class); } }); } } catch (Exception ex) { Log.e("ATHTAG", "Error during authentication", ex); } finally { try { if (iso14443 != null && iso14443.isConnected()) { iso14443.close(); } } catch (IOException e) { e.printStackTrace(); } } }).start(); }); AlertDialog alert = builder.create(); alert.setCancelable(false); // 禁止用户取消,确保完成验证 alert.show(); }); } } else { System.out.println("[ERR] Failed to select applet !!!"); } } catch (Exception ex) { Log.e("ATHTAG", "Error found during tag comms", ex); } finally { try { if (iso14443 != null && iso14443.isConnected()) { iso14443.close(); } } catch (IOException e) { e.printStackTrace(); } } }).start(); } }
3. 核心改进点
- UI与耗时操作分离:所有NFC通信、认证逻辑放在后台线程,主线程仅处理Dialog显示和用户交互,避免UI阻塞。
- 用户交互驱动流程:Dialog添加确认按钮,只有用户点击后才执行后续认证,保证用户有足够时间验证挑战码。
- 线程安全保障:所有UI操作通过
runOnUiThread执行,避免跨线程操作UI的异常。 - 资源安全回收:通过
finally块确保NFC连接被正确关闭,避免资源泄漏。
内容的提问来源于stack exchange,提问作者thotheolh
相关产品推荐
相关产品推荐

