Android 13下Xamarin Forms NFC扫描触发应用选择窗口问题求助
问题描述
使用Xamarin Forms开发应用,采用Android 31 SDK编译后,在Android 13设备上启动应用时,NFC读取流程会弹出应用选择窗口,但其他Android版本无此现象。
已配置的清单权限:
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.BIND_NFC_SERVICE" /> <uses-permission android:name="android.permission.NFC_TRANSACTION_EVENT" />
监听代码:
var intent = new Intent(this, this.GetType()).AddFlags(ActivityFlags.SingleTop); this.NFCdevice.EnableForegroundDispatch( this, PendingIntent.GetActivity(this, 0, intent, 0), new[] { new IntentFilter(NfcAdapter.ActionTechDiscovered) }, new[] { new[] { NFCTechs.Ndef }, new[] { NFCTechs.MifareClassic }, new[] { NFCTechs.MifareUltralight }, new[] { NFCTechs.NdefFormatable }, new[] { NFCTechs.NfcA } });
已尝试操作:
- 添加应用NFC需求声明:
[assembly: UsesFeature("android.hardware.nfc", Required = true)] - 补充监听
NFCTechs.NfcB、NFCTechs.NfcF、NFCTechs.NfcV格式,但问题仍存在。
解决方案
1. 适配Android 13的PendingIntent标志位
Android 13(API 33)对PendingIntent的创建逻辑做了调整,原代码中使用的0标志位在高版本系统中无法保证前台调度的正常触发,需替换为PendingIntentFlags.Mutable(目标API 31+必须使用该标志,系统要求明确指定Intent是否可修改):
var intent = new Intent(this, this.GetType()).AddFlags(ActivityFlags.SingleTop); var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.Mutable); this.NFCdevice.EnableForegroundDispatch( this, pendingIntent, new[] { new IntentFilter(NfcAdapter.ActionTechDiscovered) }, new[] { new[] { NFCTechs.Ndef }, new[] { NFCTechs.MifareClassic }, new[] { NFCTechs.MifareUltralight }, new[] { NFCTechs.NdefFormatable }, new[] { NFCTechs.NfcA }, new[] { NFCTechs.NfcB }, new[] { NFCTechs.NfcF }, new[] { NFCTechs.NfcV } });
2. 完善IntentFilter覆盖范围
仅监听ActionTechDiscovered可能无法覆盖所有NFC标签的触发场景,建议同时添加ActionTagDiscovered和ActionNdefDiscovered,确保系统能将NFC事件优先分发给你的应用:
var filters = new[] { new IntentFilter(NfcAdapter.ActionTechDiscovered), new IntentFilter(NfcAdapter.ActionTagDiscovered), new IntentFilter(NfcAdapter.ActionNdefDiscovered) }; // 若标签包含NDEF数据,需为ActionNdefDiscovered添加MIME类型过滤 var ndefFilter = new IntentFilter(NfcAdapter.ActionNdefDiscovered); try { ndefFilter.AddDataType("*/*"); // 匹配所有MIME类型,可根据业务需求调整为具体类型 } catch (IntentFilter.MalformedMimeTypeException e) { throw new RuntimeException("无效的MIME类型过滤器", e); } // 将ndefFilter加入filters数组
3. 确保Activity的LaunchMode配置匹配
在AndroidManifest.xml中,处理NFC事件的Activity需设置launchMode="singleTop",与代码中ActivityFlags.SingleTop保持一致,避免重复创建Activity实例导致前台调度失效:
<activity android:name=".MainActivity" android:launchMode="singleTop"> <!-- 其他配置 --> </activity>
4. 规范ForegroundDispatch的生命周期管理
必须在Activity的OnResume方法中启用前台调度,OnPause方法中禁用,确保应用在前台时才拦截NFC事件,避免后台时系统触发应用选择窗口:
protected override void OnResume() { base.OnResume(); if (NFCdevice != null && NFCdevice.IsEnabled) { var intent = new Intent(this, this.GetType()).AddFlags(ActivityFlags.SingleTop); var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.Mutable); var filters = new[] { new IntentFilter(NfcAdapter.ActionTechDiscovered), new IntentFilter(NfcAdapter.ActionTagDiscovered), new IntentFilter(NfcAdapter.ActionNdefDiscovered) }; var techLists = new[] { new[] { NFCTechs.Ndef }, new[] { NFCTechs.MifareClassic }, new[] { NFCTechs.MifareUltralight }, new[] { NFCTechs.NdefFormatable }, new[] { NFCTechs.NfcA }, new[] { NFCTechs.NfcB }, new[] { NFCTechs.NfcF }, new[] { NFCTechs.NfcV } }; NFCdevice.EnableForegroundDispatch(this, pendingIntent, filters, techLists); } } protected override void OnPause() { base.OnPause(); if (NFCdevice != null) { NFCdevice.DisableForegroundDispatch(this); } }
5. 验证TechLists的完整性
确认TechLists包含目标NFC标签支持的所有技术类型,部分NFC标签可能同时支持多种技术标准(如MifareClassic同时属于NfcA),遗漏技术类型会导致系统无法匹配,进而弹出应用选择窗口。可以通过读取标签的TechList来确认:
// 在OnNewIntent中获取标签并打印支持的技术 protected override void OnNewIntent(Intent intent) { base.OnNewIntent(intent); var tag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag; if (tag != null) { var techs = tag.GetTechList(); // 打印techs,查看标签支持的所有技术,补充到TechLists中 } }
内容的提问来源于stack exchange,提问作者Pastaru

