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

news2024/10/6 0:30:05

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; }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI日报|苹果将在iOS 18中引入ChatGPT,联想或成AI PC最大受益者

文章推荐 AI日报&#xff5c;阿里8亿美元购入月之暗面36%股份&#xff0c;Meta首席杨立昆建议不要研究大模型 阿里通义降价&#xff0c;百度文心免费&#xff0c;一图对比谁是最具性价比大模型&#xff1f; 苹果与OpenAI达成协议&#xff1a;将在iOS 18中提供ChatGPT聊天机器…

服务器数据恢复—EVA存储异常断电重启后虚拟机无法启动如何恢复数据?

服务器存储数据恢复环境&#xff1a; 某品牌EVA8400&#xff0c;服务器上安装VMware ESXi虚拟化平台&#xff0c;虚拟机的虚拟磁盘包括数据盘&#xff08;精简模式&#xff09;快照数据盘&#xff0c;部分虚拟机中运行oracle数据库和mysql数据库。 服务器存储故障&检测&…

精准数据提取:提升业务分析与决策效率

在当今信息爆炸的时代&#xff0c;数据已经成为企业运营和决策的核心驱动力。然而&#xff0c;面对海量的数据&#xff0c;如何快速、准确地提取出有价值的信息&#xff0c;成为了摆在众多企业面前的一大挑战。本文将探讨如何通过精准数据提取来提升业务分析与决策的效率。 一…

数据中台建设方案(Word版源文档)

建设大数据管理中台&#xff0c;按照统一的数据规范和标准体系&#xff0c;构建统一数据采集&#xfe63;治理&#xfe63;共享标准、统一技术开发体系、统一接口 API &#xff0c;实现数据采集、平台治理&#xff0c;业务应用三层解耦&#xff0c;并按照统一标准格式提供高效的…

有趣的css - 双开门按钮

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是一个双开门的按钮&#xff0c;交互效果比较强&#xff0c;但是实现很简单&#xff0c;快学起来吧。 最新文章通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码cs…

【Redis】 关于 Redis 集合类型

文章目录 &#x1f343;前言&#x1f333;普通命令&#x1f6a9;sadd&#x1f6a9;smembers&#x1f6a9;sismember&#x1f6a9;scard&#x1f6a9;spop&#x1f6a9;smove&#x1f6a9;srem &#x1f332;集合间操作&#x1f6a9;sinter&#x1f6a9;sinterstore&#x1f6a9…

Serpens3通过 运行脚本,向python传参

def main(a):print(a)#pid等变量名,需要和serpens中同名 main(pid)若.py文件要运行更多的逻辑&#xff0c;可以传参定义执行哪个函数 如何将执行完成的python返回参数给serpens3

2024年湖北水平能力测试能搞定吗?

武汉中级职称报名正式高一段落&#xff0c;意味着今年武汉市中级职称报名除开东湖高新区之外&#xff0c;其余地方都已经正式截止了&#xff0c;那么接下来大家都在准备6月中下旬的水平能力测试考试。 水平能力测试分为两种&#xff1a;面试答辩或者笔试机考试卷&#xff0c;面…

嵌入式进阶——温湿度传感器

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 DHT11温湿度模块原理图官方参考电路数据线通讯协议单总线传送数据位定义数据格式:校验位数据定义 协议实现 DHT11温湿度模块 DHT1…

哇!数据中台竟是企业数字化转型的关键力量!

在当今数字化浪潮席卷的时代&#xff0c;数据中台正成为企业实现数字化转型的关键力量。那么&#xff0c;究竟什么是数据中台呢&#xff1f;它乃是一种持续让企业数据活跃起来的机制&#xff0c;能够将企业内各部分数据汇聚至一个平台&#xff0c;达成数据的统一化管理。 数据中…

idea中快速找到当前git地址

idea中快速找到当前git地址 然后双击就可以看到地址了

apexcharts数据可视化之饼图

apexcharts数据可视化之饼图 有完整配套的Python后端代码。 本教程主要会介绍如下图形绘制方式&#xff1a; 基础饼图单色饼图图片饼图 基础饼图 import ApexChart from react-apexcharts;export function SimplePie() {// 数据序列const series [44, 55, 13, 43, 22]// …

IEEE Account个人姓名修改方法,5分钟解决!

一、背景 之前在注册IEEE Account时&#xff0c;最基础的first name是名&#xff0c;last name是姓都搞错了&#xff0c;太粗心了。然后发现IEEE账户的姓名不能随便修改&#xff0c;需要联系IEEE support center&#xff0c;然后经过一系列探索&#xff0c;发现下面两种方法可…

微软如何打造数字零售力航母系列科普11 - 什么是Microsoft Fabric中的数据工程?

什么是Microsoft Fabric中的数据工程&#xff1f; 目录 1. Lakehouse(湖边小屋) 2. Apache Spark Job Definition (作业定义) 3. Notebook(笔记本) 4. Data Pipeline (数据管道) Microsoft Fabric中的数据工程使用户能够设计、构建和维护基础架构和系统&#xff0c;使其组…

电机控制系列模块解析(24)—— 飞车转速跟踪

转速跟踪启动&#xff1a;又名顺风&&逆风启动、或者飞车启动、或者启动前转速检测。应用背景见附录。 转速跟踪 也可以理解为 对正在高速运行的电机 进行初始位置辨识。 一、转速跟踪方案 转速跟踪是电机控制中的一项关键技术&#xff0c;尤其在变频驱动、伺服系统等…

WebGIS 智慧城市三维可视化综合管控

智慧城市可视化建设不仅提升了城市管理的科技含量和效率&#xff0c;还促进了城市可持续发展&#xff0c;提升了居民的生活质量。随着技术的不断发展和应用&#xff0c;智慧城市可视化建设将会更加丰富和完善&#xff0c;为城市发展带来更加广阔的前景。 图扑应用自研 HT for W…

推荐一个实用的ETF短线交易策略

就短线交易策略来说&#xff0c;ETF是一种很好的工具&#xff0c;流动性充足&#xff0c;交易成本低廉&#xff0c;没有印花税&#xff0c;买卖一个回合的手续费0.02%就够了&#xff0c;甚至更低&#xff0c;而股票卖出时&#xff0c;光印花税就要收0.1%&#xff0c;买卖一个回…

MyBatis多数据源配置与使用,基于ThreadLocal+AOP

导读 MyBatis多数据源配置与使用其一其二1. 引依赖2. 配置文件3. 编写测试代码4. 自定义DynamicDataSource类5. DataSourceConfig配置类6. AOP与ThreadLocal结合7. 引入AOP依赖8. DataSourceContextHolder9. 自定义注解UseDB10. 创建切面类UseDBAspect11. 修改DynamicDataSourc…

Spark Sql写代码方式(yarn)以及 spark sql整合hive详解

引入部分&#xff1a;通常我们在IDEA中写spark代码中如果设置了loacl参数&#xff0c;基本都是在IDEA本地运行&#xff0c;不会提交到 standalone或yarn上运行&#xff0c;在前几篇文章中写的大多数都是该种形式的spark代码&#xff0c;但也写到了如何将spark代码提交到standal…

如何防止锂电池反充

锂电池通常用于许多需要备用电源的设备应用中&#xff0c;例如实时时钟 (RTC) 和存储设备。当锂电池不是电路中的单一电源时&#xff0c;如果电池意外连接到可为电池充电的电源&#xff0c;则存在火灾或爆炸的风险。本应用笔记提供了在备用电源开关电路中连接锂电池所需的信息&…