Android访问sd卡权限,各个版本有不同的方式。使用ARFoundation编程android项目时,可以使用unity提供的Permission申请android权限,提示有UnauthorizedAccessException的报错,基本为权限问题。
Android中sd卡的路径一般为:/storage/emulated/0/…
比如/storage/emulated/0/DCIM 为相机目录。
从《ARFoundation从零开始3-arfoundation项目》创建项目
1.安卓6.0(API 23)以下,访问只需要在清单文件中添加如下权限:
2. 在安卓6.0(API 23)及以上系统,还需要在代码中动态申请权限:
ActivityCompat.requestPermissions(MainActivity.this, permissions, PERMISSION_REQUEST);
3. 安卓10系统以上,还需要在清单文件中application节点加上:android:requestLegacyExternalStorage="true"
1.新增自定义项AndroidManifest.xml,用来设置android权限的配置。Edit-ProjectSettings-Player-Publishing Settings,勾选自定义Manifest:
2.进入当前项目目录\Assets\Plugins\Android,修改AndroidManifest.xml文件:
修改主Activity为com.unity3d.player.MainActivity,文件内容修改为:
2.进入当前项目\Assets\Plugins\Android目录,新建MainActivity.java文件,作为主启动Activity:
3. 进入当前项目\Assets\Plugins\Android目录,mainTemplate.gradle.meta文件repositories修改为:
google()
mavenCentral()
与android studio中保持一致!
4.unity中-Project Settings-Player-Other Settings-Write Permission选择External:
5.MainActivity.java代码:
package com.unity3d.player;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.Toast;
public class MainActivity extends UnityPlayerActivity{
private static MainActivity instance;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
}
public static MainActivity GetInstance()
{
return instance;
}
@Override public boolean dispatchKeyEvent(KeyEvent event)
{
if(event.getKeyCode() == KeyEvent.KEYCODE_BACK){
onBackPressed();
return true;
}
if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
return mUnityPlayer.injectEvent(event);
return super.dispatchKeyEvent(event);
}
//------
@Override
public void onBackPressed() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mUnityPlayer.quit();
}
});
super.onBackPressed();
}
//unity调用android的方法
public int Sum(int x, int y)
{
return x + y;
}
public String OnUnityFinished(String str){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(
instance,
"OnUnityFinished=" + str,
Toast.LENGTH_LONG).show();
}
});
return "ok";
}
public void CallUnityFun(String str)
{
String receiveObj = "MainCamera";//unity中脚本挂载的Object的name
String receiveMethod = "Receive";//unity中Object挂载脚本的方法名
String params = str + " Android Call Unity.";//方法要穿的参数
//android调用unity,
UnityPlayer.UnitySendMessage(receiveObj, receiveMethod, params);
}
}
Unity中自带的Android权限申请类Permission:
Unity - Scripting API: Permission
其中主要用到的方法:
HasUserAuthorizedPermission方法用来判断是否获得了该权限:
Permission.HasUserAuthorizedPermission("android.permission.READ_PHONE_STATE")
RequestUserPermission方法用来请求该权限:
Permission.RequestUserPermission("android.permission.READ_PHONE_STATE");
1.编写android权限申请类,项目Scripts文件夹下新建AndroidPermissions.cs文件,挂载到AR Session Origin下:
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Android;
namespace FrameworkDesign.Example
{
public class AndroidPermissions : MonoBehaviour
{
private static AndroidPermissions instance;
private int index;
private List<string> permissionList = new List<string>();
private void Awake()
{
if(instance == null)
{
instance = this;
Init();
}
}
//初始化,权限申请要尽可能早
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public void Init()
{
//权限添加进列表
permissionList.Clear();
index = 0;
//permissionList.Add("android.permission.READ_PHONE_STATE");
permissionList.Add(Permission.ExternalStorageRead);
permissionList.Add(Permission.ExternalStorageWrite);
// permissionList.Add(Permission.FineLocation);
// permissionList.Add(Permission.CoarseLocation);
StartCheckPermission(0.02f); //开始申请
Logger.Log("权限申请完毕");
}
public void StartCheckPermission(float time)
{
//Logger.Log("开始权限申请");
if (permissionList.Count > 0)
{
Get(permissionList[index], time);
}
}
///
/// 判断并申请权限
///
/// name="type">权限名
/// name="time">如拒绝延迟多久再次申请
void Get(string type, float time)
{
if (!Permission.HasUserAuthorizedPermission(type))
{
Permission.RequestUserPermission(type);
Logger.Log("正在获取的权限:" + type);
StartCoroutine(Check(type, time));
}
else
{
Logger.Log("权限已经获取:" + type);
if (index < permissionList.Count - 1)
{
index += 1;
Get(permissionList[index], time);
}
}
}
IEnumerator Check(string type, float time)
{
yield return new WaitForSeconds(time);
Get(type, time);
}
}
}
2.打包运行:
当用户尚未授权时,程序弹出Android权限申请界面,点击授权即可。
unity导出lib库,(先删除原来导出的lib库文件夹下所有文件,确保修改的文件更新):
1.java JDK升级到11,配置环境变量。
2.Android studio升级到2021.2.1:下载安装包安装,提示删除旧版,点击确定安装新版。
3. Android studio配置gradle,file-project structure-project,配置7.2.1和7.3.3:
点击SDK Location-Gradle Settings-Gradle JDK,选择11:
1.复制unityLibrary到android studio工程根目录(先删除原有unityLibrary文件夹),启动android studio
2.gradle.properties文件注释掉android.enableR8=false:
#android.enableR8=false
3.app的build.gradle的buildToolsVersion :
buildToolsVersion '30.0.3'
4.打包出错:
原因是androidx兼容问题,android代码使用了androidx,删除unityLibrary的build.gradle内以下内容:
打包成功
MainActivity 继承UnityPlayerActivity,实现与unity的接口调用,相关代码都在MainActivity中编写。
如未配置,参看《ARFoundation从零开始3-arfoundation项目》。
安装运行:
对于Unity工程不单独打包,而是接入到其他android工程里的情况,android权限的申请最好在android端进行,不能确保在android申请的时候再在unity端申请。
Android端申请权限代码:
//----1.android端申请权限,以下代码放到当前Activity类里-----------------
//------2.在当前Activity的onCreate方法里调用checkPermission();
String[] permissions = new String[]{Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_SETTINGS};
List mPermissionList = new ArrayList<>();
private static final int PERMISSION_REQUEST = 1;
// 检查权限
private void checkPermission() {
mPermissionList.clear();
//判断哪些权限未授予
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
mPermissionList.add(permissions[i]);
}
}
//判断是否为空
if (mPermissionList.isEmpty()) {//未授予的权限为空,表示都授予了
} else {//请求权限方法
String[] permissions = mPermissionList.toArray(new String[mPermissionList.size()]);//将List转为数组
ActivityCompat.requestPermissions(MainActivity.this, permissions, PERMISSION_REQUEST);
}
}
/**
* 响应授权
* 这里不管用户是否拒绝,都进入首页,不再重复申请权限
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQUEST:
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
}
//----android端申请权限-----------------
(unity的gradle没有与android studio同步,仍使用的gradle6.7.1+jdk1.8,没有升级到gradle7.3.3+jdk11)
1. Unity api:
Unity - Scripting API: Permission
2.ARFoundation示例:
GitHub - Unity-Technologies/arfoundation-samples: Example content for Unity projects based on AR Foundation
3.ARCore文档:
在 Unity (AR Foundation) 应用中执行光线投射 | ARCore | Google Developers