news 2026/4/12 3:41:08

【Unity】安卓相机与相册权限适配实战:从6.0到高版本兼容方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Unity】安卓相机与相册权限适配实战:从6.0到高版本兼容方案

1. 安卓权限管理机制演进与Unity适配挑战

安卓6.0引入的动态权限系统彻底改变了应用获取敏感权限的方式。我记得第一次在Unity项目里调用相机功能时,明明在AndroidManifest.xml里声明了权限,却遭遇了闪退事故。后来才发现,像相机、存储这类危险权限(Dangerous Permissions)必须运行时动态申请。

动态权限的核心机制其实很简单:应用启动时只有基础权限,需要用户敏感数据时再弹窗申请。但Unity作为跨平台引擎,默认不处理这些原生系统特性,这就导致三个典型问题:

  1. 权限弹窗闪现消失:Unity的Activity会拦截系统弹窗
  2. Android 7.0文件共享崩溃:File Uri暴露路径被禁止
  3. Android 10分区存储:直接访问外部存储受限

这里有个容易忽略的细节:不同厂商ROM对权限弹窗的处理差异很大。比如小米会默认关闭某些权限的弹窗,需要引导用户去设置页手动开启。我在华为P30上测试时,就发现相机权限弹窗的样式和原生安卓有明显区别。

2. 基础权限配置与动态请求实战

2.1 清单文件声明

首先在Assets/Plugins/Android/AndroidManifest.xml中添加必要权限声明:

<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

注意Android 10+需要添加requestLegacyExternalStorage属性应对分区存储:

<application android:requestLegacyExternalStorage="true" ... >

2.2 Unity中的动态权限请求

创建PermissionRequester.cs脚本处理动态逻辑:

#if UNITY_ANDROID using UnityEngine; using UnityEngine.Android; public class PermissionRequester : MonoBehaviour { const int CAMERA_CODE = 1001; const int STORAGE_CODE = 1002; public void RequestCameraPermission() { if (Permission.HasUserAuthorizedPermission(Permission.Camera)) { OpenCamera(); } else { var callbacks = new PermissionCallbacks(); callbacks.PermissionGranted += _ => OpenCamera(); Permission.RequestUserPermission(Permission.Camera, callbacks); } } public void RequestStoragePermission() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 6.0以下版本无需动态申请 AccessGallery(); return; } if (Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead)) { AccessGallery(); } else { var callbacks = new PermissionCallbacks(); callbacks.PermissionGranted += _ => AccessGallery(); Permission.RequestUserPermission(Permission.ExternalStorageRead, callbacks); } } void OpenCamera() { /* 相机逻辑 */ } void AccessGallery() { /* 相册逻辑 */ } } #endif

这个方案比原生Android代码简洁很多,利用了Unity 2018.3引入的PermissionAPI。但要注意几个坑点:

  1. iOS需要额外处理NSCameraUsageDescription描述
  2. 华为设备上可能需要检查CanRequestPermission()返回值
  3. 用户拒绝后再次请求时需要解释必要性

3. Android 7.0+文件共享方案

3.1 FileProvider配置

AndroidManifest.xml<application>标签内添加:

<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>

创建Assets/Plugins/Android/res/xml/file_paths.xml

<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external_files" path="."/> <cache-path name="cache_files" path="."/> </paths>

3.2 Unity与Android交互

修改相机调用代码处理版本差异:

void TakePhoto() { string path = Path.Combine(Application.persistentDataPath, "photo.jpg"); AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", "android.media.action.IMAGE_CAPTURE"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { AndroidJavaClass fileProvider = new AndroidJavaClass("androidx.core.content.FileProvider"); AndroidJavaObject file = new AndroidJavaObject("java.io.File", path); AndroidJavaObject uri = fileProvider.CallStatic<AndroidJavaObject>( "getUriForFile", GetCurrentActivity(), Application.identifier + ".fileprovider", file); intent.Call<AndroidJavaObject>("putExtra", "android.media.action.IMAGE_CAPTURE", uri); } else { AndroidJavaObject uri = new AndroidJavaObject("android.net.Uri", "file://" + path); intent.Call<AndroidJavaObject>("putExtra", "android.media.action.IMAGE_CAPTURE", uri); } GetCurrentActivity().Call("startActivityForResult", intent, PHOTO_CODE); }

这里有个实际项目中的经验:部分国产ROM会修改相机应用包名,建议先检查设备是否支持标准ACTION_IMAGE_CAPTURE

bool IsCameraAvailable() { AndroidJavaObject packageManager = GetCurrentActivity() .Call<AndroidJavaObject>("getPackageManager"); AndroidJavaObject intent = new AndroidJavaObject( "android.content.Intent", "android.media.action.IMAGE_CAPTURE"); return intent.Call<bool>("resolveActivity", packageManager) != null; }

4. 完整实现方案与优化建议

4.1 相册访问最佳实践

对于相册访问,推荐直接使用Intent.ACTION_GET_CONTENT避免文件路径问题:

void OpenGallery() { AndroidJavaObject intent = new AndroidJavaObject( "android.content.Intent", "android.intent.action.GET_CONTENT"); intent.Call<AndroidJavaObject>("setType", "image/*"); GetCurrentActivity().Call("startActivityForResult", intent, GALLERY_CODE); }

处理返回结果时,建议使用内容解析器获取真实路径:

// 在Android插件代码中 public static String getRealPathFromUri(Context context, Uri uri) { String path = ""; if (context.getContentResolver() != null) { try (Cursor cursor = context.getContentResolver() .query(uri, null, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { int idx = cursor.getColumnIndex(MediaStore.Images.Media.DATA); path = cursor.getString(idx); } } } return path; }

4.2 性能优化技巧

  1. 纹理压缩:大图加载前先采样

    Texture2D CompressTexture(Texture2D source, int maxSize) { int width = Mathf.Min(source.width, maxSize); int height = Mathf.Min(source.height, maxSize); var rt = RenderTexture.GetTemporary(width, height); Graphics.Blit(source, rt); var result = new Texture2D(width, height); RenderTexture.active = rt; result.ReadPixels(new Rect(0, 0, width, height), 0, 0); result.Apply(); RenderTexture.ReleaseTemporary(rt); return result; }
  2. 异步加载:避免主线程卡顿

    IEnumerator LoadImageAsync(string path) { using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(path)) { yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { Texture2D texture = DownloadHandlerTexture.GetContent(request); // 使用纹理... } } }
  3. 缓存管理:使用Application.persistentDataPath存储用户图片,定期清理缓存

4.3 厂商适配经验

  • 小米设备:在AndroidManifest.xml添加:

    <meta-data android:name="xiaomi.permission.CAMERA_USE_SYSTEM_UI" android:value="true"/>
  • 华为设备:检查HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable()

  • OPPO/Realme:需要单独申请Settings.ACTION_MANAGE_OVERLAY_PERMISSION

在华为Mate40 Pro上测试时,发现相册返回的Uri格式与其他设备不同,需要额外处理content://com.huawei.hidisk.fileprovider/开头的路径。这种厂商差异在实际开发中很常见,建议准备主流设备的真机测试方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/8 9:14:27

M9A智能辅助系统技术白皮书:《重返未来:1999》自动化解决方案

M9A智能辅助系统技术白皮书&#xff1a;《重返未来&#xff1a;1999》自动化解决方案 【免费下载链接】M9A 重返未来&#xff1a;1999 小助手 项目地址: https://gitcode.com/gh_mirrors/m9a/M9A 一、自动化需求与系统架构 核心价值&#xff1a;解析游戏自动化本质矛盾…

作者头像 李华
网站建设 2026/4/3 2:59:35

Lychee Rerank MM快速部署:免conda环境、纯Docker镜像一键拉起重排序服务

Lychee Rerank MM快速部署&#xff1a;免conda环境、纯Docker镜像一键拉起重排序服务 1. 为什么你需要一个真正的多模态重排序服务 你有没有遇到过这样的问题&#xff1a;在做图文搜索时&#xff0c;用传统文本向量检索返回的结果&#xff0c;图片和文字描述明明很相关&#…

作者头像 李华
网站建设 2026/4/10 2:17:57

惊艳效果展示:Nano-Banana生成的工业产品拆解图集

惊艳效果展示&#xff1a;Nano-Banana生成的工业产品拆解图集 1 工业视觉工程的新范式&#xff1a;从“画图”到“拆解” 你有没有见过这样一张图——一台咖啡机被精准地“炸开”&#xff0c;所有内部零件悬浮在空中&#xff0c;彼此间距一致、朝向统一、标注清晰&#xff0c…

作者头像 李华
网站建设 2026/3/26 11:42:43

Anything to RealCharacters 2.5D转真人引擎:数字藏品平台内容生成基础设施

Anything to RealCharacters 2.5D转真人引擎&#xff1a;数字藏品平台内容生成基础设施 1. 项目概述 1.1 核心功能定位 Anything to RealCharacters 2.5D转真人引擎是一款专为数字藏品平台设计的图像转换工具&#xff0c;能够将2.5D、卡通和二次元风格的图像高质量转换为写实…

作者头像 李华
网站建设 2026/3/27 22:50:14

Qwen2.5-VL-7B-Instruct快速上手指南:上传图片+输入指令=3步完成物体定位

Qwen2.5-VL-7B-Instruct快速上手指南&#xff1a;上传图片输入指令3步完成物体定位 1. 工具简介 Qwen2.5-VL-7B-Instruct是一款专为RTX 4090显卡优化的多模态视觉交互工具&#xff0c;基于阿里通义千问官方模型开发。它能够理解图片内容并回答相关问题&#xff0c;支持OCR提取…

作者头像 李华