执行NFC长耗时操作时频繁触发ANR问题排查求助
核心问题点
1. 耗时NFC操作被错误放到主线程执行
CardPairingActivity.continueExec()方法中,使用默认Handler将API.cardAuthenticate()和API.readAndStoreNFCUserProfileData()这类耗时NFC操作抛回主线程执行,直接阻塞UI线程导致ANR。默认Handler创建时会绑定当前线程的Looper,这里handler在Activity主线程初始化,所以post的Runnable会在主线程运行。
2. 同时启用两种NFC监听模式引发冲突
代码中同时启用了Foreground Dispatch和Reader Mode两种NFC监听机制:
onCreate和onResume中同时调用nfcAdapter.enableForegroundDispatch()和nfcAdapter.enableReaderMode()- 两种模式会同时监听NFC事件,导致回调逻辑混乱,可能引发资源竞争或线程阻塞。
3. StrictMode被全局禁用,主线程违规操作无法被检测
代码中设置StrictMode.ThreadPolicy.Builder().permitAll(),关闭了Android对主线程耗时操作的检测,导致主线程执行NFC操作的问题无法被及时发现,掩盖了ANR的根本原因。
4. 弹窗关闭后直接触发主线程耗时操作
AlertCallbackDialog.onDismiss()方法中直接调用continueExec(),而continueExec()又将耗时操作放回主线程,再次阻塞UI线程。
5. 静态变量持有Activity上下文,可能引发内存泄漏
CardPairingActivity中的myContext是静态变量,持有Activity实例,当Activity销毁时无法被GC回收,长期运行会导致内存泄漏,间接引发ANR。
修复方案
1. 将耗时NFC操作移至后台线程
修改continueExec(),用新线程执行耗时操作,仅在需要更新UI时通过Handler回调主线程:
@RequiresApi(api = Build.VERSION_CODES.O) public static void continueExec() { // 新线程执行耗时NFC操作 new Thread(() -> { System.out.println("Authenticating to card ..."); System.out.println("Card User Role: " + (int) role); System.out.println("Card Username: " + username); System.out.println("Card Secret: " + password); try { if (API.cardAuthenticate(Constants.USER_KEYMAN, username, password.toCharArray())) { // 执行大量数据读取 API.readAndStoreNFCUserProfileData(); // 成功后在主线程更新UI new Handler(Looper.getMainLooper()).post(() -> { // 这里处理跳转、成功提示等UI操作 }); } else { System.out.println("[ERR] Card Login FAILED ..."); int tries = API.adminCardLoginTriesRemaining(role, null); // 失败弹窗必须在主线程显示 new Handler(Looper.getMainLooper()).post(() -> { Context context = myContextRef.get(); if (context != null) { if (tries != -1) { SharedResource.showNotificationDialog("Card Pairing Failed", "Card Login Failed. \r\nLogin Retries Left: " + tries, "OK", context, MainActivity.class); } else { SharedResource.showNotificationDialog("Card Pairing Failed", "Card Login Failed.", "OK", context, MainActivity.class); } } }); } // 操作完成后关闭NFC连接 if (SharedResource.getIsoDep() != null) { try { SharedResource.getIsoDep().close(); SharedResource.setIsoDep(null); } catch (IOException e) { Log.e("ATHTAG", "Error closing NFC connection", e); } } } catch (Exception e) { Log.e("ATHTAG", "Error during card operation", e); // 错误提示在主线程显示 new Handler(Looper.getMainLooper()).post(() -> { Context context = myContextRef.get(); if (context != null) { SharedResource.showNotificationDialog("Error", "Card operation failed", "OK", context, MainActivity.class); } }); } }).start(); }
2. 只保留一种NFC监听模式
选择Reader Mode(更适配ISO-14443-4标签场景),移除Foreground Dispatch相关代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_card_pairing); // 用弱引用持有上下文,避免内存泄漏 myContextRef = new WeakReference<>(this); callBack = new PairingCallBack(); callBack.setContext(this); NfcManager nfcManager = (NfcManager) getSystemService(Context.NFC_SERVICE); nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter != null) { nfcAdapter.enableReaderMode(this, callBack, READER_FLAGS, null); System.out.println("NFC Adapter ready ..."); } if (nfcManager != null) { System.out.println("NFC Manager ready ..."); } } @Override protected void onResume() { super.onResume(); if (nfcAdapter != null) { nfcAdapter.enableReaderMode(this, callBack, READER_FLAGS, null); } }
3. 移除StrictMode全局禁用代码
删除StrictMode.ThreadPolicy相关配置,让Android系统自动检测主线程的耗时操作,帮助快速定位问题。
4. 用弱引用持有Activity上下文
替换静态myContext为弱引用,避免内存泄漏:
// 替换静态myContext为弱引用 private static WeakReference<Context> myContextRef;
内容的提问来源于stack exchange,提问作者thotheolh

