Unity + Android:如何请求多个权限?

我正在使用目标平台Android进行Unity(版本2019.1.11)项目,我需要具有以下权限才能运行我的应用:

android.permission.CAMERA
android.permission.RECORD_AUDIO
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_PHONE_STATE

Unity文档指定this方式来请求Android权限。我的目标是在应用启动期间对所有必需的权限进行初步检查,如下所示:

private void AskPermissions()
{
#if UNITY_ANDROID
    if (!Permission.HasUserauthorizedPermission(Permission.microphone))
    {
        Permission.RequestUserPermission(Permission.microphone);
    }
    if (!Permission.HasUserauthorizedPermission(Permission.Camera))
    {
        Permission.RequestUserPermission(Permission.Camera);
    }
    if (!Permission.HasUserauthorizedPermission(Permission.ExternalStorageWrite))
    {
        Permission.RequestUserPermission(Permission.ExternalStorageWrite);
    }
    if (!Permission.HasUserauthorizedPermission("android.permission.READ_PHONE_STATE"))
    {
        Permission.RequestUserPermission("android.permission.READ_PHONE_STATE");
    }
#endif
}

但是,这不起作用:该应用程序仅显示未授权的第一个权限的对话框,也不显示随后被检查的未授权的对话框。

如何确保始终检查所有权限?

hftxc 回答:Unity + Android:如何请求多个权限?

以我为例,我使用了单位的 OnApplicationFocus 回调函数。当权限窗口打开时,应用程序将失去其焦点模式;当权限窗口关闭(用户接受或拒绝权限)时,应用程序将再次获得其焦点模式。每次 OnApplicationFocus 回调调用。

它似乎很脏,但效果很好。您还必须在清单文件中添加权限。

Please see this GitHub link for see the full project

public class AndroidPermissionHandler : MonoBehaviour
{
    bool isItPermissionTime = false;
    string nextPermission;
    Stack<string> permissions = new Stack<string>();

void Start()
{
    OpenAllPermissions();
}

public void OpenAllPermissions()
{
    isItPermissionTime = true;
    CreatePermissionList();

}
void CreatePermissionList()
{
    permissions = new Stack<string>();
    permissions.Push(Permission.ExternalStorageWrite);
    permissions.Push(Permission.Camera);
    permissions.Push(Permission.CoarseLocation);
    AskForPermissions();
}
 void AskForPermissions ()
{
    if (permissions == null || permissions.Count <= 0)
    {
        isItPermissionTime = false;
        return;
    }
    nextPermission = permissions.Pop();

    if (nextPermission == null)
    {
        isItPermissionTime = false;
        return;
    }
    if (Permission.HasUserAuthorizedPermission(nextPermission) == false)
    {
        Permission.RequestUserPermission(nextPermission);
    }
    else
    {
        if (isItPermissionTime == true)
            AskForPermissions();
    }
    Debug.Log("Unity>> permission " + nextPermission + "  status ;" + Permission.HasUserAuthorizedPermission(nextPermission));
}

private void OnApplicationFocus(bool focus)
{
    Debug.Log("Unity>> focus ....  " + focus + "   isPermissionTime : " + isItPermissionTime);
    if (focus == true && isItPermissionTime == true)
    {
        AskForPermissions();
    }
}
,

Permission.RequestUserPermission似乎以某种方式异步工作,并且在已经显示对话框的情况下不会显示对话框-因此,在找到第一个未经授权的权限之后,只需跳过所有其他权限。
我可以绕过这样的问题:

private IEnumerator AskForPermissions()
{
#if UNITY_ANDROID
    List<bool> permissions = new List<bool>() { false,false,false };
    List<bool> permissionsAsked = new List<bool>() { false,false };
    List<Action> actions = new List<Action>()
    {
        new Action(() => {
            permissions[0] = Permission.HasUserAuthorizedPermission(Permission.Microphone);
            if (!permissions[0] && !permissionsAsked[0])
            {
                Permission.RequestUserPermission(Permission.Microphone);
                permissionsAsked[0] = true;
                return;
            }
        }),new Action(() => {
            permissions[1] = Permission.HasUserAuthorizedPermission(Permission.Camera);
            if (!permissions[1] && !permissionsAsked[1])
            {
                Permission.RequestUserPermission(Permission.Camera);
                permissionsAsked[1] = true;
                return;
            }
        }),new Action(() => {
            permissions[2] = Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite);
            if (!permissions[2] && !permissionsAsked[2])
            {
                Permission.RequestUserPermission(Permission.ExternalStorageWrite);
                permissionsAsked[2] = true;
                return;
            }
        }),new Action(() => {
            permissions[3] = ermission.HasUserAuthorizedPermission("android.permission.READ_PHONE_STATE");
            if (!permissions[3] && !permissionsAsked[3])
            {
                Permission.RequestUserPermission("android.permission.READ_PHONE_STATE");
                permissionsAsked[3] = true;
                return;
            }
        })
    };
    for(int i = 0; i < permissionsAsked.Count; )
    {
        actions[i].Invoke();
        if(permissions[i])
        {
            ++i;
        }
        yield return new WaitForEndOfFrame();
    }
#endif
}
,

众所周知,Android运行时权限很麻烦,在您的情况下,问题是Android的权限请求UI面板在您的第一个Permission.RequestUserPermission上占据了前台,这会挂起您的App并阻止执行以下代码

到目前为止,我提出的用于处理运行时权限的最干净的解决方案是在协程中运行无限循环,该协程贪婪地检查所需的权限,并提示用户授予每个权限(如果不是)

乍一看听起来很丑陋,但请考虑一下,当您请求权限时,由于代码无法正常工作的相同原因而中断了循环:由于权限请求UI面板占据了前台,并且您的应用程序被暂停,当焦点被放回时,您的循环将再次开始以检查缺少的权限,此外,您还可以使用yield return新的WaitForSeconds(0.2f)

来减慢循环的速度

这是经过修饰的代码,用于通知用户如果未授予所有必需的权限,则无法继续进行操作:

private bool _locationPermissionAsked;
private bool _microphonePermissionAsked;
private bool _cameraPermissionAsked;
private bool _storagePermissionAsked;

private void Start()
{
#if UNITY_ANDROID && !UNITY_EDITOR
    StartCoroutine(RequestPermissionsRoutine());        
#else
    /***** Ready to run you app *****/
#endif
}

private IEnumerator RequestPermissionsRoutine()
{
    yield return new WaitForEndOfFrame();
    while (true)
    {
        // For each permission you need,build a block like the following,it could 
        // have been done dynamically but given the small ammount of possible options 
        // I preferred to keep it extended
        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) && !_locationPermissionAsked)
        {
            // This flag keeps track of the user choice against the permission panel
            //
            // if he choose to NOT grant the permission we skip the permission request
            // because we are gonna notify him that he needs to grant the permission later 
            // using a message in our App
            _locationPermissionAsked = true; 

            // You can even ask permissions using android literal definition instead of Unity's Permission.FineLocation
            yield return Permission.RequestPermission("android.permission.ACCESS_FINE_LOCATION").WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Microphone) && !_microphonePermissionAsked)
        {
            _microphonePermissionAsked = true;
            yield return Permission.RequestPermission(Permission.Microphone).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Camera) && !_cameraPermissionAsked)
        {
            _cameraPermissionAsked = true;
            yield return Permission.RequestPermission(Permission.Camera).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite) && !_storagePermissionAsked)
        {
            _storagePermissionAsked = true;
            yield return Permission.RequestPermission(Permission.ExternalStorageWrite);
            continue;
        }

        // This is the part where we check if all the permissions were granted
        // and allow the user to run the App ( else condition )
        // or prompt him to grant the permissions he denied
        //
        // Note that this code is never reached before each permission have been asked 
        // once,because of the "continue;"s used before
        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) ||
            !Permission.HasUserAuthorizedPermission(Permission.Microphone) ||
            !Permission.HasUserAuthorizedPermission(Permission.Camera) ||
            !Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
        {
            if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
            {
                /***** Tell the user to grant FineLocation Permission *****/
            }

            if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
            {
                /***** Tell the user to grant Microphone Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.Camera))
            {
                /***** Tell the user to grant Camera Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
            {
                /***** Tell the user to grant ExternalStorageWrite Permission *****/
            }

            /***** This is where all permissions have been asked once  *****/
            /***** and one or more were NOT granted,*****/
            /***** you can write the code to handle this fallback here *****/
        }
        else
        {
            // I like to give some time to the Android looper before running my App,just to be safe
            yield return new WaitForSeconds(1f); 

            /***** Ready to run you App *****/
        }

        // Slow down the loop by a little bit,not strictly needed but not harmful either
        yield return new WaitForSeconds(0.2f);
    }
}

即使在用户拒绝一个许可然后强行杀死您的应用程序,或者其他一些应用程序意外地抢占了前台以及到目前为止我遇到的其他各种风险案例的情况下,这仍然可以工作

我要加上一点: 如果您使用的是Google ARCore,请注意它会覆盖原始的Unity权限请求机制(https://github.com/google-ar/arcore-unity-sdk/issues/151),鼓励开发人员使用自己的GoogleARCore.AndroidPermissionsManager而不是UnityEngine.Android.Permission,因此UnityEngine.Android.Permission将不起作用,您仍然可以使用此脚本,但是您需要将所有“ Permission.RequestUserPermission”替换为“ AndroidPermissionsManager.RequestUserPermission”(您无需替换“ Permission.HasUserAuthorizedPermission”,它们仍然可以工作),甚至更好,我可以为您做到:

private IEnumerator RequestPermissionsRoutine()
{
    yield return new WaitForEndOfFrame();
    while (true)
    {
        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) && !_locationPermissionAsked)
        {
            _locationPermissionAsked = true; 
            yield return AndroidPermissionsManager.RequestPermission("android.permission.ACCESS_FINE_LOCATION").WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Microphone) && !_microphonePermissionAsked)
        {
            _microphonePermissionAsked = true;
            yield return AndroidPermissionsManager.RequestPermission(Permission.Microphone).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Camera) && !_cameraPermissionAsked)
        {
            _cameraPermissionAsked = true;
            yield return AndroidPermissionsManager.RequestPermission(Permission.Camera).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite) && !_storagePermissionAsked)
        {
            _storagePermissionAsked = true;
            yield return AndroidPermissionsManager.RequestPermission(Permission.ExternalStorageWrite);
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) ||
            !Permission.HasUserAuthorizedPermission(Permission.Microphone) ||
            !Permission.HasUserAuthorizedPermission(Permission.Camera) ||
            !Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
        {
            if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
            {
                /***** Tell the user to grant FineLocation Permission *****/
            }

            if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
            {
                /***** Tell the user to grant Microphone Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.Camera))
            {
                /***** Tell the user to grant Camera Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
            {
                /***** Tell the user to grant ExternalStorageWrite Permission *****/
            }
            /***** This is where all permissions have been asked once  *****/
            /***** and one or more were NOT granted,*****/
            /***** you can write the code to handle this fallback here *****/
        }
        else
        {
            yield return new WaitForSeconds(1f); 

            /***** Ready to run you App *****/
        }

        yield return new WaitForSeconds(0.2f);
    }
}
,

我正在从事的项目遇到相同的问题。 一个简单的解决方法是在每个权限之间使用协程请求权限,并在其中等待几秒钟的收益回报。或者只是在应用程序的不同位置请求权限。例如,仅在用户将要使用它时才请求相机许可,或者在用户将要访问它时只写外部存储器。由于这些是敏感权限,因此最好让用户知道为什么要求他事先授予这些权限的原因。

IEnumerator Start() {
        // Ask for camera permission
        if(!Permission.HasUserAuthorizedPermission(Permission.Camera)) {
            Permission.RequestUserPermission(Permission.Camera);
        }
        yield return new WaitForSeconds(2.5f);
        // Ask for external storage permission
        if(!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite)) {
            Permission.RequestUserPermission(Permission.ExternalStorageWrite);
        }
}

上面的代码正在工作。您可以调整“等待秒数”值,并添加所需的权限。请注意,只有在尚未授予权限的情况下,我才会请求权限。

干杯!

本文链接:https://www.f2er.com/3152864.html

大家都在问