“.NET MAUI – ‘Permission request must be invoked on main thread'”

huangapple go评论64阅读模式
英文:

.NET MAUI - "Permission request must be invoked on main thread"

问题

在Android应用的主线程上请求权限访问时出现了困惑,因为它需要是异步的。我希望在我的应用程序打开时立即执行此操作,但目前我得到以下错误:“必须在主线程上调用权限请求。”我该如何处理这种情况?我的理解是,RequestAsync需要在单独的线程上运行(因为它是异步方法调用)。

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("需要权限", "因为!!!", "OK");
            }

            LocationPermission = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
        }
        catch (Exception ex)
        {
            // 错误:权限请求必须在主线程上 **
            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
英文:

I am confused on how I am supposed to request permissions access on the main thread of an android app if it needs to be async. I want to do this as soon as my application opens, but as is I am getting the following error: &quot;Permission request must be invoked on main thread.&quot; How do I do this? My understanding is that RequestAsync requires a separate thread (Since it's an async method call).

public partial class SplashPage : ContentPage
{
    PermissionStatus LocationPermission;

    public SplashPage()
    {
    	InitializeComponent();
        LocationPermission = PermissionStatus.Unknown;
    }

    protected override void OnAppearing()
    {
        var result = Task.Run(async () =&gt; await CheckLocationPermission());
        result.Wait();
        var resultval = result.Result;

        result = Task.Run(async () =&gt; await RequestLocationPermission());
        result.Wait();
    }

    public async Task&lt;PermissionStatus&gt; CheckLocationPermission()
    {
        LocationPermission = await Permissions.CheckStatusAsync&lt;Permissions.LocationWhenInUse&gt;();
        return LocationPermission;
    }

    public async Task&lt;PermissionStatus&gt; RequestLocationPermission()
    {
        try
        {
            if (LocationPermission == PermissionStatus.Granted)
                return LocationPermission;

            if (Permissions.ShouldShowRationale&lt;Permissions.LocationWhenInUse&gt;())
            {
                await Shell.Current.DisplayAlert(&quot;Needs Permissions&quot;, &quot;BECAUSE!!!&quot;, &quot;OK&quot;);
            }

            LocationPermission = await Permissions.RequestAsync&lt;Permissions.LocationWhenInUse&gt;();
        }
        catch (Exception ex)
        {
            //Error that permissions request must be on main thread **
            Console.WriteLine(ex.Message);
        }

        return LocationPermission;
    }
}

AppShell.xaml:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;Shell
    x:Class=&quot;CellularSignal1.AppShell&quot;
    xmlns=&quot;http://schemas.microsoft.com/dotnet/2021/maui&quot;
    xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
    xmlns:local=&quot;clr-namespace:CellularSignal1&quot;
    Shell.FlyoutBehavior=&quot;Disabled&quot;&gt;
    
    &lt;TabBar x:Name=&quot;MyTabBar&quot;&gt;
        &lt;Tab x:Name=&quot;CellularInfo&quot; Title=&quot;Cellular Info&quot;&gt;
            &lt;ShellContent ContentTemplate=&quot;{DataTemplate local:SplashPage}&quot; Route=&quot;SplashPage&quot;/&gt;
            &lt;ShellContent ContentTemplate=&quot;{DataTemplate local:CellularInfo}&quot; Route=&quot;CellularInfo&quot;/&gt;
            &lt;ShellContent ContentTemplate=&quot;{DataTemplate local:BSInfo}&quot; Route=&quot;BSInfo&quot;/&gt;
        &lt;/Tab&gt;
    &lt;/TabBar&gt;
&lt;/Shell&gt;

AppShell.xaml.cs:

namespace CellularSignal1;

#if ANDROID
public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
    }
}
    
#endif

答案1

得分: 6

你应该将OnAppearing()方法覆盖为async并使用await来调用。Task.Run()创建的线程池线程不是必需的:

protected override async void OnAppearing()
{
    var resultCheck = await CheckLocationPermission();
    var resultRequest = await RequestLocationPermission();
}

一般来说,当你遇到像上面提到的引用主线程的错误时,这并不意味着代码需要自己的线程,而是代码必须在MainThread上执行,这是一个特殊的线程。

你可以从任何不是主线程的线程上调用主线程上的代码,像这样:

private async Task SomeMethodAsync()
{
    await MainThread.InvokeOnMainThreadAsync(async () => 
    {
        var resultCheck = await CheckLocationPermission();
        var resultRequest = await RequestLocationPermission();
    });
}

注意:在你的情况下,后一种方法不应该是必要的,因为OnAppearing()已经在主线程上执行。

英文:

You should make the OnAppearing() method override async and await the calls. There's no need for the thread pool thread created by Task.Run():

protected override async void OnAppearing()
{
    var resultCheck = await CheckLocationPermission();
    var resultRequest = await RequestLocationPermission();
}

In general, when you encounter an error like the one above referring to the main thread, it doesn't mean that the code requires its own thread, but rather that the code must be executed on the MainThread, which is a special thread.

You can invoke code on the main thread from any other thread, which isn't the main thread, like this:

private async Task SomeMethodAsync()
{
    await MainThread.InvokeOnMainThreadAsync(async () =&gt; 
    {
        var resultCheck = await CheckLocationPermission();
        var resultRequest = await RequestLocationPermission();
    });
}

Note: In your case, the latter shouldn't be necessary, because OnAppearing() already executes on the main thread.

答案2

得分: 3

你正在使用Essentials,所以你可以使用InvokeOnMainThreadAsync。我通常使用这个助手来执行相同的操作:

public static class PermissionsHelper
{
    public static async Task<PermissionStatus> CheckAndRequestPermissionAsync<TPermission>()
        where TPermission : BasePermission, new()
    {
        return await MainThread.InvokeOnMainThreadAsync(async () =>
        {
            TPermission permission = new TPermission();
            var status = await permission.CheckStatusAsync();
            if (status != PermissionStatus.Granted)
            {
                status = await permission.RequestAsync();
            }

            return status;
        });
    }
}

用法:

var status = await PermissionsHelper.CheckAndRequestPermissionAsync<Permissions.LocationWhenInUse>();
if (status != PermissionStatus.Granted)
{
    // 你可以做任何你喜欢的事情
}
英文:

You are using Essentials, so you could InvokeOnMainThreadAsync. I usually use this helper to do the same:

public static class PermissionsHelper
{
    public static async Task&lt;PermissionStatus&gt; CheckAndRequestPermissionAsync&lt;TPermission&gt;()
        where TPermission : BasePermission, new()
    {
        return await MainThread.InvokeOnMainThreadAsync(async () =&gt;
        {
            TPermission permission = new TPermission();
            var status = await permission.CheckStatusAsync();
            if (status != PermissionStatus.Granted)
            {
                status = await permission.RequestAsync();
            }

            return status;
        });
    }
}

Usage:

var status = await PermissionsHelper.CheckAndRequestPermissionAsync&lt;Permissions.LocationWhenInUse&gt;();
if (status != PermissionStatus.Granted)
{
    //whatever you like 
}

huangapple
  • 本文由 发表于 2023年2月27日 03:16:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75574429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定