Xamarin.Forms适配Android 13时存储权限弹窗不显示问题求助
我用Visual Studio 2022开发Xamarin.Forms安卓项目,把android:targetSdkVersion从29升级到33后,在Android 13设备上运行时,权限请求弹窗完全不出现。以下是我的相关代码:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.myapp.app" android:versionCode="23" android:versionName="12.6.1"> <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="33" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-feature android:name="android.hardware.telephony" android:required="false" /> <uses-feature android:name="android.hardware.location" android:required="false" /> <uses-feature android:name="android.hardware.location.gps" android:required="false" /> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.location.network" android:required="false" /> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CALL_PHONE" android:required="false" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <application android:allowBackup="false" android:requestLegacyExternalStorage="true" android:label="FieldDirect" android:largeHeap="true"> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/icon_transparent" /> <meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/brown" /> <meta-data android:name="com.google.android.geo.API_KEY" android:value="Adfsgrgsdvewsdvevwdvsdvsdv" /> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" /> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="${applicationId}" /> </intent-filter> </receiver> <service android:name="crc6494e14b9856016c30.PNFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.myapp.app.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_path" /> </provider> </application> </manifest>
App.xaml.cs
if (context != null) { if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) { if ((Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.Camera) != Android.Content.PM.Permission.Granted) && (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.ReadExternalStorage) != Android.Content.PM.Permission.Granted) && (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.WriteExternalStorage) != Android.Content.PM.Permission.Granted) && (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessCoarseLocation) != Android.Content.PM.Permission.Granted) && (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessFineLocation) != Android.Content.PM.Permission.Granted) && (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.CallPhone) != Android.Content.PM.Permission.Granted) && (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.ReadPhoneState) != Android.Content.PM.Permission.Granted) && (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.ReadPhoneNumbers) != Android.Content.PM.Permission.Granted)) { ActivityCompat.RequestPermissions(context, new String[] { Manifest.Permission.Camera, Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage, Manifest.Permission.AccessCoarseLocation, Manifest.Permission.AccessFineLocation, Manifest.Permission.ReadPhoneNumbers, Manifest.Permission.ReadPhoneState, Manifest.Permission.CallPhone }, 100); } } }
解决方案
1. 修复权限判断逻辑(核心问题)
你的权限判断用了&&逻辑,意思是只有当所有权限都未被授予时才会触发请求。只要有一个权限已经被授予,整个条件就不成立,弹窗自然不会出现。
把判断条件改成||,只要有任意一个权限未授予,就发起请求:
if ((Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.Camera) != Android.Content.PM.Permission.Granted) || (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.ReadExternalStorage) != Android.Content.PM.Permission.Granted) || (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.WriteExternalStorage) != Android.Content.PM.Permission.Granted) || (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessCoarseLocation) != Android.Content.PM.Permission.Granted) || (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessFineLocation) != Android.Content.PM.Permission.Granted) || (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.CallPhone) != Android.Content.PM.Permission.Granted) || (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.ReadPhoneState) != Android.Content.PM.Permission.Granted) || (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(context, Manifest.Permission.ReadPhoneNumbers) != Android.Content.PM.Permission.Granted))
2. 适配Android 13(API 33)权限变更
Android 13对部分权限做了拆分和替换,需要更新Manifest和请求逻辑:
存储权限:
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE在API33中被弃用,需要根据你的使用场景添加对应的媒体权限,比如:<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />如果需要兼容API32及以下设备,保留原来的
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE,同时添加新的媒体权限。电话权限:Android 13中
READ_PHONE_STATE不再包含读取电话号码的权限,你已经单独申请了READ_PHONE_NUMBERS,这部分没问题,但要确保请求时包含该权限。
3. 确保权限请求的上下文正确
在Xamarin.Forms中,建议在MainActivity的OnCreate或OnResume方法中发起权限请求,而不是App.xaml.cs——App.xaml.cs中的context可能不是有效的Activity上下文,会导致权限请求无法正常触发弹窗。
修改示例(在MainActivity中):
protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // 其他初始化代码... RequestPermissionsIfNeeded(); } private void RequestPermissionsIfNeeded() { if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) { List<string> permissionsToRequest = new List<string>(); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.Camera); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessCoarseLocation) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.AccessCoarseLocation); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.AccessFineLocation); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.CallPhone) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.CallPhone); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadPhoneState) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.ReadPhoneState); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadPhoneNumbers) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.ReadPhoneNumbers); // 适配Android 13存储权限 if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Tiramisu) { if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadMediaImages) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.ReadMediaImages); } else { if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.ReadExternalStorage); if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != Android.Content.PM.Permission.Granted) permissionsToRequest.Add(Manifest.Permission.WriteExternalStorage); } if (permissionsToRequest.Count > 0) { ActivityCompat.RequestPermissions(this, permissionsToRequest.ToArray(), 100); } } }
4. 处理权限请求结果
记得重写OnRequestPermissionsResult方法来处理用户的权限选择:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults) { base.OnRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 100) { // 处理权限授予结果,比如检查哪些权限被授予 for (int i = 0; i < permissions.Length; i++) { if (grantResults[i] == Android.Content.PM.Permission.Granted) { // 权限已授予,执行对应逻辑 } else { // 权限被拒绝,提示用户或做降级处理 } } } }
内容的提问来源于stack exchange,提问作者Manthiram

