Unity 权限 之 Android 【权限 动态申请】功能的简单封装

Unity 权限 之 Android 【权限 动态申请】功能的简单封装

目录

Unity 权限 之 Android 【权限 动态申请】功能的简单封装

一、简单介绍

二、Android 权限 动态申请

三、实现原理

四、注意事项

五、案例实现简单步骤

附录:

一、进一步优化

二、多个权限申请代码参考


一、简单介绍

Unity 是一个功能强大的跨平台游戏引擎,广泛用于开发视频游戏和其他实时3D互动内容,如模拟器和虚拟现实应用。

  1. 游戏引擎

    • Unity:Unity Technologies 开发的跨平台游戏引擎,支持2D和3D图形、物理引擎、音频、视频、网络和多平台发布。
    • 跨平台支持:Unity 支持在多个平台上发布,包括 Windows、macOS、Linux、iOS、Android、WebGL、PlayStation、Xbox、Switch 等。
  2. 开发环境

    • Unity Editor:用于创建和管理 Unity 项目的集成开发环境(IDE)。开发者可以在其中创建场景、设计关卡、编写代码和调试游戏。
    • 场景(Scene):Unity 中的基本构建块,一个场景可以被视为一个关卡或一个游戏中的独立部分。
  3. 编程语言

    • C#:主要编程语言。Unity 使用 C# 脚本来控制游戏对象和实现游戏逻辑。
    • UnityScript(已弃用):曾经支持的 JavaScript 变种,但已经被弃用。

Unity 进阶开发涉及更复杂的技术和更深入的知识,以创建高性能、复杂和专业的游戏和应用程序。以下是一些 Unity 进阶开发的关键领域和技术:

  • 设计模式

    • 学习和应用常见的设计模式(如单例模式、工厂模式、观察者模式)以便更好地组织和管理代码。
  • 异步编程

    • 使用 C# 的 asyncawait 关键字进行异步编程,以提高应用的响应速度和性能。
  • 依赖注入

    • 使用依赖注入(如 Zenject)来管理对象的依赖关系,减少耦合,提高代码的可测试性和可维护性。

二、Android 权限 动态申请

在开发 Unity Android 应用时,动态获取权限是一个常见的需求。由于 Android 6.0(API 级别 23)引入了运行时权限模型,应用必须在运行时请求某些权限。下面是一个如何在 Unity 中封装动态获取权限功能的示例。

在 Unity 中,仅使用 C# 代码来处理 Android 动态权限请求是可能的,但仍需要调用 Android 的原生 API。这可以通过 AndroidJavaClassAndroidJavaObject 来实现。

三、实现原理

PermissionManager 是一个用于管理 Android 权限请求的单例类。其主要功能是动态请求权限并在用户响应后处理结果。以下是其工作原理和实现步骤的详细解释:

  1. 单例模式

    • PermissionManager 使用单例模式确保在整个应用生命周期中只有一个实例。
  2. 权限请求接口

    • RequestPermission 方法用于请求特定的权限。
    • 首先检查权限是否已经授予,如果已授予则立即调用回调函数返回结果。
    • 如果权限未授予,启动协程 RequestAndCheckPermission 请求权限并等待用户响应。
  3. 协程处理

    • RequestAndCheckPermission 方法通过协程 IEnumerator 实现异步等待。
    • 使用 Permission.RequestUserPermission 方法请求权限。
    • 使用 yield return new WaitForSeconds(1.0f) 等待一段时间,让用户有足够时间响应权限请求。
    • 等待结束后,再次检查权限状态并调用回调函数返回结果。

四、注意事项

  1. 权限列表

    • 确保在 AndroidManifest.xml 中声明所有需要请求的权限,否则请求可能会失败。
    • 例如:
      <uses-permission android:name="android.permission.CAMERA" />
  2. 权限请求的异步性

    • 用户响应权限请求的时间是不确定的。使用协程 (IEnumerator) 异步等待用户响应而不阻塞主线程是必要的。
    • yield return new WaitForSeconds(1.0f) 是一个简单的等待机制,实际项目中可能需要更复杂的逻辑来处理用户的响应。
  3. 回调函数

    • 确保传递给 RequestPermission 的回调函数能正确处理权限被授予或拒绝的情况。
    • 回调函数签名为 System.Action<string, bool>,其中第一个参数是权限名,第二个参数是权限是否被授予的布尔值。
  4. 用户体验

    • 提示用户为什么需要请求特定权限,以提高用户接受权限请求的概率。
    • 在权限被拒绝后,提供适当的反馈和处理机制,如再次请求权限或退出相关功能。
  5. 多权限请求

    • 当前示例处理单个权限请求。如果需要请求多个权限,可以扩展 PermissionManager 类以支持批量请求,并分别处理每个权限的授予结果。

五、案例实现简单步骤

1、新建一个 Unity 工程

2、新建脚本 PermissionWrapper 封装权限申请功能

3、新建脚本 TestPermissionWrapper 测试封装权限申请功能,申请 camera 权限

4、把 TestPermissionWrapper  挂载到场景中

5、在 Player Settings 中的 Build 中勾选 Custom Main Manifest ,添加 Camera 权限申请

6、打包运行

六、关键代码

1、PermissionWrapper


using UnityEngine;
using UnityEngine.Android;

public class PermissionWrapper : MonoSingleton<PermissionWrapper>
{
    /// <summary>
    /// 请求权限
    /// </summary>
    /// <param name="permission">权限名称</param>
    /// <param name="callback">权限回调:true 权限获取成功回调,false 权限获取失败回调</param>
    public void RequestPermission(string permission, System.Action<string, bool> callback)
    {
        if (Permission.HasUserAuthorizedPermission(permission))
        {
            callback?.Invoke(permission, true);
        }
        else
        {
            StartCoroutine(RequestAndCheckPermission(permission, callback));
        }
    }

    /// <summary>
    /// 协程请求权限
    /// </summary>
    /// <param name="permission">权限名称</param>
    /// <param name="callback">权限回调:true 权限获取成功回调,false 权限获取失败回调</param>
    /// <returns></returns>
    private System.Collections.IEnumerator RequestAndCheckPermission(string permission, System.Action<string, bool> callback)
    {
        Permission.RequestUserPermission(permission);
        yield return new WaitForSeconds(1.0f);

        bool granted = Permission.HasUserAuthorizedPermission(permission);
        callback?.Invoke(permission, granted);
    }
}

2、TestPermissionWrapper


using UnityEngine;
using UnityEngine.Android;

public class TestPermissionWrapper : MonoBehaviour
{
    private void Start()
    {
        // 请求摄像头权限
        PermissionWrapper.Instance.RequestPermission(Permission.Camera, OnPermissionResult);
    }

    private void OnPermissionResult(string permission, bool granted)
    {
        if (granted)
        {
            Debug.Log("权限已授予: " + permission);
            // 执行需要权限的功能
        }
        else
        {
            Debug.Log("权限被拒绝: " + permission);
            // 处理权限被拒绝的情况
        }
    }
}

3、MonoSingleton

using System;
using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
    private static T m_Instance;

    public static T Instance
    {
        get
        {
            if ((UnityEngine.Object)m_Instance == (UnityEngine.Object)null)
            {
                T[] array = UnityEngine.Object.FindObjectsOfType<T>();
                if (array != null && array.Length != 0)
                {
                    if (array.Length != 1)
                    {
                        throw new Exception($"## Uni Exception ## Cls:{typeof(T)} Info:Singleton not allows more than one instance");
                    }

                    m_Instance = array[0];
                }
                else
                {
                    m_Instance = new GameObject($"{typeof(T).ToString()}(Singleton)").AddComponent<T>();
                    m_Instance.OnSingletonInit();
                }
            }

            return m_Instance;
        }
    }

    protected virtual void Awake()
    {
        m_Instance = this as T;
    }

    protected virtual void OnDestroy()
    {
        m_Instance = null;
    }

    protected virtual void OnSingletonInit()
    {
    }
}

4、AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">
    
    <!--相机权限-->
    <uses-permission android:name="android.permission.CAMERA" />
  
    <application>
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:theme="@style/UnityThemeSelector">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>

附录:

一、进一步优化

  • 用户提示:在请求权限前,先提示用户为何需要该权限,增强用户接受度。
  • 多权限支持:实现多权限同时请求的逻辑。
  • 超时处理:考虑在等待用户响应时添加超时处理逻辑,避免无限期等待。
  • 权限变更监听:实现权限变更监听,当用户在应用设置中更改权限时做出相应处理。

通过这些改进和注意事项,可以确保权限请求功能更加健壮和用户友好。

二、多个权限申请代码参考

1、PermissionJudgeMgr

using FfalconXR;
using FFALFramework.UIModule;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Android;

public class PermissionJudgeMgr : MonoSingleton<PermissionJudgeMgr>
{
    #region Data
    /// <summary>
    /// TAG
    /// </summary>
    const string TAG = "[PermissionJudgeMgr] ";

	/// <summary>
	/// 权限申请列表
	/// </summary>
	List<IRequestPermissionItem> m_RequestPermissionItemLst;

    /// <summary>
    /// 请求权限回调
    /// </summary>
    private PermissionCallbacks m_PermissionCallbacks;

    /// <summary>
    /// 全部权限允许后的事件
    /// </summary>
    public Action OnAllPermissionGrantedAction;

    /// <summary>
    /// 判断是否有权限
    /// </summary>
    public bool IsHasPermission { get { return JudgeIsHasPermission(); } }
    #endregion

    #region public function

    /// <summary>
    /// 检测 权限
    /// </summary>
    public void CheckAppPermission()
    {
        CheckRequestPermissionLst();
    }

    #endregion

    #region 生命周期
    /// <summary>
    /// OnSingletonInit
    /// </summary>
    protected override void OnSingletonInit()
    {
        base.OnSingletonInit();
        
        RegisterPermissionListener();
    }
    /// <summary>
    /// OnDestroy
    /// </summary>
    protected override void OnDestroy()
    {
        base.OnDestroy();
        UnRegisterPermissionListener();
    }

    #endregion

    #region private function

    /// <summary>
    /// 检测权限队列
    /// </summary>
    private void CheckRequestPermissionLst() {

        IRequestPermissionItem requestPermissionItem = null;
        foreach (var item in m_RequestPermissionItemLst)
        {
            if (item.IsHasPermission == false)
            {
                requestPermissionItem = item;
                break;
            }
        }

        if (requestPermissionItem != null)
        {
            requestPermissionItem.RequestPermission(m_PermissionCallbacks);
        }
        else
        {
            OnAllPermissionGrantedAction?.Invoke();
        }
    }

    /// <summary>
    /// 注册权限监听
    /// </summary>
    private void RegisterPermissionListener()
    {
        UIManager.Instance.QuitAll();

        AddRequestPermissionItem();

        m_PermissionCallbacks = new PermissionCallbacks();
        //对系统权限处理的回调
        m_PermissionCallbacks.PermissionGranted += OnPermissionGranted;
        m_PermissionCallbacks.PermissionDenied += OnPermissionDenied;
    }

    /// <summary>
    /// 取消权限监听
    /// </summary>
    private void UnRegisterPermissionListener()
    {
        m_PermissionCallbacks.PermissionGranted -= OnPermissionGranted;
        m_PermissionCallbacks.PermissionDenied -= OnPermissionDenied;
        m_PermissionCallbacks = null;
        OnAllPermissionGrantedAction = null;
        m_RequestPermissionItemLst.Clear();
        m_RequestPermissionItemLst = null;
    }

    /// <summary>
    /// 权限授予事件
    /// </summary>
    /// <param name="Permission"></param>
    private void OnPermissionGranted(string Permission)
    {
        MainThreadQueue.Instance.ExecuteQueue.Enqueue(() =>
        {
            Debug.Log(TAG+"OnPermissionGranted():" + Permission);
            
            // 继续请求其他权限
            CheckRequestPermissionLst();

        });
    }

    /// <summary>
    /// 权限拒绝事件
    /// </summary>
    /// <param name="Permission"></param>
    private void OnPermissionDenied(string Permission)
    {
        MainThreadQueue.Instance.ExecuteQueue.Enqueue(() =>
        {
            Debug.Log(TAG + "OnPermissionDenied():" + Permission);

            // Todo 提示:1、打开失败页界面;2、然后可以继续请求权限
            
        });
    }

    /// <summary>
    /// 添加权限申请判断
    /// </summary>
    private void AddRequestPermissionItem() {
        m_RequestPermissionItemLst = new List<IRequestPermissionItem>();

        // 相机权限获取
        m_RequestPermissionItemLst.Add(new CameraRequestPermissionItem());

        // 麦克风权限获取
        m_RequestPermissionItemLst.Add(new MicrophoneRequestPermissionItem());

        // 继续添加更多...
    }

    /// <summary>
    /// 判断是否有权限没有申请
    /// </summary>
    /// <returns></returns>
    private bool JudgeIsHasPermission() {
        if (m_RequestPermissionItemLst == null) return true;
        else {
            foreach (var item in m_RequestPermissionItemLst)
            {
                if (item.IsHasPermission == false) return false;
            }
        }

        return true;
    }

    #endregion
}

2、CameraRequestPermissionItem


using UnityEngine.Android;

public class CameraRequestPermissionItem : BaseRequestPermissionItem
{
    /// <summary>
    /// TAG
    /// </summary>
    protected override string TAG { get; } = "[CameraRequestPermissionItem] ";

    /// <summary>
    /// 去申请的权限名称
    /// </summary>
    public override string ToRequestPermissionName => Permission.Camera;
}

3、MicrophoneRequestPermissionItem

using UnityEngine.Android;

public class MicrophoneRequestPermissionItem : BaseRequestPermissionItem
{
    /// <summary>
    /// TAG
    /// </summary>
    protected override string TAG { get; } = "[MicrophoneRequestPermissionItem] ";

    /// <summary>
    /// 去申请的权限名称
    /// </summary>
    public override string ToRequestPermissionName => Permission.Microphone;
}

4、BaseRequestPermissionItem


using UnityEngine;
using UnityEngine.Android;

public class BaseRequestPermissionItem : IRequestPermissionItem
{
    #region Data

    /// <summary>
    /// TAG
    /// </summary>
    protected virtual string TAG { get; } = "[BaseRequestPermissionItem] ";

    /// <summary>
    /// 去申请的权限名称
    /// </summary>
    public virtual string ToRequestPermissionName => Permission.Camera;

    /// <summary>
    /// 判断是否有权限
    /// </summary>
    public virtual bool IsHasPermission => Permission.HasUserAuthorizedPermission(ToRequestPermissionName);

    #endregion

    #region Interface function

    /// <summary>
    /// 请求权限
    /// </summary>
    /// <param name="permissionCallbacks">权限回调事件</param>
    public virtual void RequestPermission(PermissionCallbacks permissionCallbacks)
    {
        if (permissionCallbacks == null) {
            Debug.LogError(TAG + " RequestPhoneStatePermission() permissionCallbacks is null ");
            return;
        }

        if (!IsHasPermission)
        {
            Debug.Log(TAG+ " RequestPhoneStatePermission() ");
            Permission.RequestUserPermission(ToRequestPermissionName, permissionCallbacks);
        }
    }

    #endregion
}

5、IRequestPermissionItem



using UnityEngine.Android;

public interface IRequestPermissionItem 
{
    /// <summary>
    /// 去申请的权限名称
    /// </summary>
    string ToRequestPermissionName { get; }

    /// <summary>
    /// 请求权限
    /// </summary>
    /// <param name="permissionCallbacks">权限回调事件</param>
    void RequestPermission(PermissionCallbacks permissionCallbacks);

    /// <summary>
    /// 判断是否有权限
    /// </summary>
    bool IsHasPermission { get; }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/649372.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

5款好用的AI写作软件,一键生成高质量文章

在当今信息快速发展的时代&#xff0c;AI写作软件逐渐成为创作者们的得力助手。它们能够凭借先进的技术和算法&#xff0c;一键生成高质量的文章&#xff0c;为创作者们节省大量的创作时间和精力。以下是5款备受好评的AI写作软件&#xff0c;下面在本文中分享给大家&#xff0c…

物联网网关在电梯按需维保方案中起到什么作用?梯联网网关

为减少电梯故障和预防电梯事故&#xff0c;保障人身和财产安全&#xff0c;基于物联网技术的电梯按需维保已在全国多地陆续推行&#xff0c;做到了电梯安全隐患预测式排查&#xff0c;处理问题更具科学性、针对性和精准性&#xff0c;有效提升了电梯运行的安全性。那么&#xf…

深入理解一下栈

1、栈&#xff1a;数据结构 为什么 main()方法 最先执行&#xff0c;最后结束&#xff1f; 当然是因为 main()方法入栈啦。 2、栈&#xff1a;栈内存&#xff0c;主管程序的运行&#xff0c;生命周期和现成同步&#xff1b; 线程结束&#xff0c;栈内内存也就释放了&#xff0c…

VasDolly图形工具-Android多渠道打包福利

简介 基于腾讯VasDolly最新版本3.0.6的图形界面衍生版本&#xff0c;旨在更好的帮助开发者构建多渠道包 使用 下载并解压工具包&#xff0c;找到Startup脚本并双击启动图形界面&#xff08;注意&#xff1a;本地需安装java环境&#xff09; 渠道格式说明 txt文件&#xff…

测试基础06:软件产品的运行环境dev、sit、test、fat、uat、pre、pro

​​​​​​​课程大纲 1、Dev开发环境 &#xff08;Development environment&#xff09; 使用者 开发人员使用。 用途 用于编程&#xff0c;版本变动很大。 外部能否访问 外部用户无法访问。 2、sit/ITE系统集成测试环境 &#xff08;System Integration Testing en…

MySQL--内置函数

一、时间函数 简单使用&#xff1a; 函数也可以嵌套使用&#xff1a; 示例&#xff1a;给当前时间加上50天 它也会自动帮我们计算相加后正确的日期。除了加天数&#xff0c;还可以加分钟&#xff0c;秒这些。 示例&#xff1a;计算新中国成立到现在经过了多少天 select date…

宝塔部署前后端分离项目手册

文章目录 安装宝塔安装环境开始部署1. 前端Vue项目1.先本地启动前端项目&#xff08;记住端口号&#xff09;2.打包前端项目3.上传前端项目4.创建PHP站点5.安全里开放端口号6.测试前端 2. 后端boot项目1. 先在本地跑起来2.修改数据库的配置信息3. 项目打包4. nohup启动项目4.1 …

字符串函数(2)<C语言>

前言 快一周没更博客了&#xff0c;最近有点忙&#xff0c;今天闲下来了&#xff0c;还是不行&#xff0c;继续干&#xff0c;书接上回继续介绍字符串函数&#xff1a;strncpy()、strncat()、strcmp()、strtok()使用、strstr()使用以及模拟实现、strerror()使用。 strncpy()、s…

如何查看网站的cookie?

前言&#xff1a; 在网络世界中&#xff0c;cookie是一种常见的信息存储方式。 对于开发者和普通用户来说&#xff0c;了解如何查看CSDN的cookie是非常重要的。 本文将介绍几种常用的方法&#xff0c;帮助大家更好地理解和使用cookie&#xff1a; 代码示例&#xff1a; 通过浏…

油猴(Tampermonkey)如何实现网站的自动化操作

1. 安装油猴插件&#xff1a;首先&#xff0c;在你使用的浏览器上安装油猴插件。油猴支持多种浏览器&#xff0c;如Chrome、Firefox等。你可以在对应的插件商店中搜索并安装油猴插件。 2. 创建用户脚本&#xff1a;打开油猴插件的管理界面&#xff0c;点击"新建脚本"…

人类行为验证处理方案 —— 脱离UI组件库实现登录、注册+表单校验

目录 01: 构建登录模块基础UI结构 02: 表单校验实现原理与方案分析 表单校验的实现原理 自定义表单校验方案分析 文章中的方案实现 03: 基于 vee-validate 实现普适的表单校验 04: 什么是人类行为验证&#xff1f;它的目的、实现原理、构建方案分别是什么&am…

结合Django和Vue.js构建现代Web应用

文章目录 1. 创建Django项目2. 配置Django后端3. 创建Vue.js前端4. 连接Django和Vue.js5. 构建和部署 在现代Web开发中&#xff0c;结合后端框架和前端框架是非常常见的&#xff0c;其中Django作为一种流行的Python后端框架&#xff0c;而Vue.js则是一种灵活强大的前端框架。本…

一、Nginx详解和安装

目录 一、简介 1、什么是Nginx 2、Nginx的优点 二、四大应用场景 1、HTTP服务器 2、反向代理 3、负载均衡 4、动静分离 三、Nginx 源码安装 1、安装相关依赖 2、启动nginx 3、安装成系统服务 一、简介 1、什么是Nginx Nginx是一款轻量级的Web服务器&#xff0c;反向…

艾体宝干货 | 用于故障排除的最佳 Wireshark 过滤器

在网络故障排除过程中&#xff0c;Wireshark是一款非常强大的工具&#xff0c;它可以用来分析网络数据包并解决各种问题。本文将介绍一些好用的Wireshark过滤器&#xff0c;以便更有效地进行故障排除。 分析网络行为和排除网络故障就像用漏斗过滤渣滓。因此&#xff0c;网络协…

如何给出好的“文言一心”指令?

一、文言一心是什么&#xff1f; 在现代技术背景下&#xff0c;“文言一心”还是百度公司创建的一款大语言模型。这款模型基于飞桨深度学习平台和文心知识增强大模型&#xff0c;并拥有强大的中文语料库&#xff0c;可以理解和生成富含文化内涵和哲理的文本内容。其核心技术架构…

MahApps.Metro的MVVM模式解析(二) 主题功能

MahApps.Metro的MVVM模式解析&#xff08;二&#xff09; 主题功能 MahApps.Metro是一个开源的WPF框架&#xff0c;旨在为WPF应用程序提供现代和漂亮的用户界面。 在MahApps.Metro中提供了切换主题的功能。经过多日的筛选和分析&#xff0c;在本文来理清它的脉络。 1 主题功…

数据结构-二叉树系统性学习(四万字精讲拿捏)

前言 这里我会从二叉树的概念开始讲解&#xff0c;其次涉及到概念结构&#xff0c;以及堆的实现和堆排序。 目的是&#xff0c;堆比二叉树简单&#xff0c;同时堆本质上是二叉树的其中一种情况&#xff0c;堆属于二叉树顺序结构的实现 最后完善二叉树的讲解&#xff0c;也就是…

认知觉醒:情绪绝对是财富的拦路虎……

认知觉醒 无论是投资还是做生意&#xff0c;跟随大众情绪就一定会亏损&#xff0c;老百姓没有是非认知&#xff0c;只有好恶&#xff0c;所以就很容易被人操控情绪。随便一个社会热点事件&#xff0c;就比如最近的涂磊事件、郭有才事件&#xff0c;打开视频的评论区&#xff0…

浅谈JMeter环境变量设置

JMeter环境变量设置 确保已安装Java Development Kit (JDK) JMeter需要Java运行环境&#xff0c;因此请先安装JDK&#xff0c;并确认JAVA_HOME环境变量已经设置好。可以通过命令提示符输入java -version和javac -version来验证是否安装成功及其版本信息&#xff0c;若没有出现…

U8G2移植到STM32,SSD13XXXOLED(硬件SPI DMA通讯)

文章目录 一、前言1.1 U8g2的特点1.2 U8G2的优势1.3 U8G2的下载地址1.4 U8g2支持的显示控制器 二、STM32Cubexm SPI DMA配置2.1 SPI设置为半双工模式2.2 SPI DMA设置2.3 oled其他引脚配置 三、移植U8G2框架3.1 精简U8G2库文件3.2 去掉csrc文件夹中无用的驱动文件3.3 文件移动到…