.NET MAUI安卓端:异步权限请求需主线程调用的实现疑问
启动时请求位置权限遇到主线程错误的解决方法
问题场景
我想在应用启动时请求位置权限,但遇到错误:"Permission request must be invoked on main thread"。我困惑的是,既然权限请求需要异步执行,该怎么在安卓应用的主线程中进行?我原本以为RequestAsync方法需要单独线程执行,以下是我的实现代码:
SplashPage代码
public partial class SplashPage : ContentPage { PermissionStatus LocationPermission; public SplashPage() { InitializeComponent(); LocationPermission = PermissionStatus.Unknown; } protected override void OnAppearing() { var result = Task.Run(async () => await CheckLocationPermission()); result.Wait(); var resultval = result.Result; result = Task.Run(async () => await RequestLocationPermission()); result.Wait(); } public async Task<PermissionStatus> CheckLocationPermission() { LocationPermission = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>(); return LocationPermission; } public async Task<PermissionStatus> RequestLocationPermission() { try { if (LocationPermission == PermissionStatus.Granted) return LocationPermission; if (Permissions.ShouldShowRationale<Permissions.LocationWhenInUse>()) { await Shell.Current.DisplayAlert("Needs Permissions", "BECAUSE!!!", "OK"); } LocationPermission = await Permissions.RequestAsync<Permissions.LocationWhenInUse>(); } catch (Exception ex) { //Error that permissions request must be on main thread ** Console.WriteLine(ex.Message); } return LocationPermission; } }
AppShell.xaml代码
<?xml version="1.0" encoding="UTF-8" ?> <Shell x:Class="CellularSignal1.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:CellularSignal1" Shell.FlyoutBehavior="Disabled"> <TabBar x:Name="MyTabBar"> <Tab x:Name="CellularInfo" Title="Cellular Info"> <ShellContent ContentTemplate="{DataTemplate local:SplashPage}" Route="SplashPage"/> <ShellContent ContentTemplate="{DataTemplate local:CellularInfo}" Route="CellularInfo"/> <ShellContent ContentTemplate="{DataTemplate local:BSInfo}" Route="BSInfo"/> </Tab> </TabBar> </Shell>
AppShell.xaml.cs代码
namespace CellularSignal1; #if ANDROID public partial class AppShell : Shell { public AppShell() { InitializeComponent(); } } #endif
核心问题分析
你错误地用Task.Run将权限请求逻辑丢到了后台线程,再通过result.Wait()阻塞主线程。但安卓系统要求权限请求必须在**主线程(UI线程)**发起,这就导致了报错。另外,Wait()会阻塞UI线程,可能引发卡顿甚至ANR。
修复方案
直接在主线程中异步执行权限请求,不需要额外开启后台线程。修改OnAppearing方法为异步版本即可:
修改后的SplashPage代码
public partial class SplashPage : ContentPage { PermissionStatus LocationPermission; public SplashPage() { InitializeComponent(); LocationPermission = PermissionStatus.Unknown; } // 将OnAppearing改为async void(生命周期方法允许此用法) protected override async void OnAppearing() { base.OnAppearing(); // 必须调用基类的OnAppearing方法 await CheckLocationPermission(); await RequestLocationPermission(); } public async Task<PermissionStatus> CheckLocationPermission() { LocationPermission = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>(); return LocationPermission; } public async Task<PermissionStatus> RequestLocationPermission() { try { if (LocationPermission == PermissionStatus.Granted) return LocationPermission; if (Permissions.ShouldShowRationale<Permissions.LocationWhenInUse>()) { await Shell.Current.DisplayAlert("需要权限", "应用需要位置权限以提供相关功能", "确定"); } // 直接在主线程异步执行权限请求 LocationPermission = await Permissions.RequestAsync<Permissions.LocationWhenInUse>(); } catch (Exception ex) { Console.WriteLine(ex.Message); } return LocationPermission; } }
关键说明
- 生命周期方法
OnAppearing可以使用async void(普通业务方法不推荐,但生命周期方法是例外),这样就能在主线程中异步执行逻辑,不会阻塞UI。 Permissions.CheckStatusAsync和Permissions.RequestAsync本身就是异步方法,在主线程调用时,await后会自动回到主线程,完全符合安卓权限请求的线程要求。- 不要用
Task.Run包装权限请求,这会把逻辑移到后台线程,违反安卓系统的规则。
内容的提问来源于stack exchange,提问作者rickster26ter1
相关产品推荐
相关产品推荐

