添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在学习《第一行代码》播放SD卡根目录中的音乐时,碰到了无法读音乐文件的问题
android.system.ErrnoException: open failed: EACCES (Permission denied)
经过查阅相关资料,现把个人的解决方法记录下来

Android 6.0(不含)以下

在6.0之前的版本,申请sd卡读写权限不是一件难事,只需要在AndroidManifest.xml中加入下列代码,权限会在app安装时申请, READ可以不添加。因为在8.0以前,申请WRITE会自动获取 一组 相关的权限,其中就包括READ权限

	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
    //READ可以不添加。因为在8.0以前,申请WRITE会自动获取 一组 相关的权限,其中就包括READ权限,因此仅申请WRITE权限也能用

Android 6.0(含)以上

Android6.0之后,Google为Android加入了运行时申请权限的机制,因此除了在AndroidManifest.xml中加入权限申请代码之外,在java代码中还要申请相关权限。Android6.0以后都要有动态申请权限

判断是否授权READ和WRITE

		int permission_write=ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);
        int permission_read=ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.READ_EXTERNAL_STORAGE);
        if(permission_write!= PackageManager.PERMISSION_GRANTED
            || permission_read!=PackageManager.PERMISSION_GRANTED){
            Toast.makeText(this, "正在请求权限", Toast.LENGTH_SHORT).show();
            //申请权限,特征码自定义为1,可在回调时进行相关判断
            ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},1);

在MainActivity.java中重写方法OnRequestPermissionResult,其中常量1是申请权限时填入的特征码

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    //权限已成功申请
                }else{
                    //用户拒绝授权
                    Toast.makeText(this, "无法获取SD卡读写权限", Toast.LENGTH_SHORT).show();
                    finish();
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);

Android 8.0申请的注意事项

在Android 8.0中,Google完善了相关机制,“所申即所得”,你申请了WRITE权限 不会同时给予READ权限,因此要对外部存储进行读写,需要申请WRITE和READ两个权限,按上边代码申请就可以了。

开发者文档:
在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。

Android 9.0申请的注意事项

在Android 9.0上即使正确使用了上边的代码,也会提示出现无权限的问题 Permission denied
从网上各处找到的资料显示:Google修改移除了 WRITE_MEDIA_STORAGE 权限相关权限,导致了外部 SD 卡存储不可写的问题
Android 9.0中sdcard 的权限和挂载问题:https://blog.csdn.net/shift_wwx/article/details/85633801
解决办法:

①适配DocumentFile

由于之前应用使用了 java.io.File 接口操作外置 SD 卡文件,期望对代码的修改量最小,则最好的方式是对已有的 File 操作再做一次封装。

由于 Android 9.0 之前系统应用默认是可以通过 java.io.File 接口写入外置 SD卡 的,而如果作为公开市场第三方应用却在 4.4 之后就不可写,而且有的厂商定制版本 Android 9.0 外置 SD 卡也是可以直接写入而不需要 DocumentFile 接口,DocumentFile 接口也没有 java.io.File 效率高。

所以最好的办法是先检查是否有文件写入权限,如果有写入权限,则直接使用 File 接口操作,如果没有权限再检查文件是否在外置 SD 卡,如果文件在 SD 卡则使用 DocumentFile 接口操作

参考自:https://blog.csdn.net/qq_36467463/article/details/88691726

②修改cpp文件,增加-w权限

alps_p0_mp2\update\alps\system\vold\model\PublicVolume.cpp

    if (!(mFusePid = fork())) {
        if (getMountFlags() & MountFlags::kPrimary) {
            if (execl(kFusePath, kFusePath,
                    "-u", "1023", // AID_MEDIA_RW
                    "-g", "1023", // AID_MEDIA_RW
                    "-U", std::to_string(getMountUserId()).c_str(),
                    "-w",
                    mRawPath.c_str(),
                    stableName.c_str(),
                    NULL)) {
                PLOG(ERROR) << "Failed to exec";
        } else {
            if (execl(kFusePath, kFusePath,
                    "-u", "1023", // AID_MEDIA_RW
                    "-g", "1023", // AID_MEDIA_RW
                    "-U", std::to_string(getMountUserId()).c_str(),
                    "-w",//add by   for add sdcard permission 就是这样
                    mRawPath.c_str(),
                    stableName.c_str(),
                    NULL)) {
                PLOG(ERROR) << "Failed to exec";

参考自:https://www.jianshu.com/p/f1329c001fd9

③修改targetSdkVersion为27(不推荐,无法在应用商店上架)

这方法看别人试过,我自己没试过。已经被这个版本的权限问题弄吐了

Android Q(10.0)申请的注意事项

早期测试版本中引入的专门用于操作媒体文件的权限 READ_MEDIA_IMAGES, READ_MEDIA_AUDIO, READ_MEDIA_VIDEO – 现已过时!

为了能给用户提供对文件的更多控制并限制文件混乱,Android Q改变了应用程序访问设备外部存储上文件的方式,例如存储在路径/ sdcard中的文件。Android Q继续使用READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,这些权限对应于存储面向用户的运行时权限。但是,默认情况下targetSdkVersion设置为Android Q的应用(以及manifest清单开启属性来启动这个变更的应用)会获得一个沙盒视图到外部存储。此类应用程序只能看到其特定于应用程序的目录和特定媒体类型,因此应用程序不需要请求任何其他用户权限。
在这里插入图片描述
在这里插入图片描述
也就是说从Android Q(10.0) sdk>=29 开始,系统为每个app提供了一个隔离环境(分区存储),参考下列资料,有十分详细的说明,这里给出解决办法

参考资料:
开发者文档
https://developer.android.google.cn/training/data-storage/files/external-scoped?hl=zh_cn

①在 AndroidManifest.xml 中加入 android:requestLegacyExternalStorage=“true”

在这里插入图片描述
加入这句代码之后会启动兼容模式:停用分区存储,但是要留意的是
警告:2020年,主要平台版本将要求所有应用都使用分区存储,无论应用的目标 SDK 级别是多少。因此,您应该提前确保您的应用能够使用分区存储。为此,请确保针对搭载 Android 10(API 级别 29)及更高版本的设备启用了该行为。

在您的应用完全兼容分区存储之前,您可以根据应用的目标 SDK 级别或 requestLegacyExternalStorage 清单属性,暂时选择停用分区存储:

以 Android 9(API 级别 28)或更低版本为目标平台。
如果以 Android 10 或更高版本为目标平台,请在应用的清单文件中将 requestLegacyExternalStorage 的值设为 true:

    <manifest ... >
      <!-- This attribute is "false" by default on apps targeting
           Android 10 or higher. -->
      <application android:requestLegacyExternalStorage="true" ... >
      </application>
    </manifest>

注意:如果某个应用在安装时启用了传统外部存储,则该应用会保持此模式,直到卸载为止。无论设备后续是否升级为搭载 Android 10 或更高版本,或者应用后续是否更新为以 Android 10 或更高版本为目标平台,此兼容性行为均适用。
要测试以 Android 9 或更低版本为目标平台的应用在使用分区存储时的行为,您可以通过将 requestLegacyExternalStorage 的值设为 false 来选择启用该行为。

②访问媒体文件,使用MediaStore。
③访问其他应用创建的任何其他文件,使用系统提供的存储访问框架

原谅本人水平有限,尚未能贴出相关代码。
但各位可以在开发者文档中找到相关的解决方法
开发者帮助文档:有关AndroidQ的适配资料

华为开发者帮助文档:https://developer.huawei.com/consumer/cn/doc/50127

请注意,华为开发者帮助文档里边有些方法已经过时,测试版时使用的方法在正式版中可能无法使用,如
早期测试版本中引入的专门用于操作媒体文件的权限
READ_MEDIA_IMAGES,
READ_MEDIA_AUDIO,
READ_MEDIA_VIDEO
现已过时!

SD卡读写权限在Android各版本有所不同,现将各版本关于sd卡读写权限申请特点记录下来并分享给大家前言在学习《第一行代码》播放SD卡根目录中的音乐时,碰到了无法读音乐文件的问题android.system.ErrnoException: open failed: EACCES (Permission denied)经过查阅相关资料,现把个人的解决方法记录下来Android 6.0(不含)以下在6.0之前的版本,申请sd卡读写权限不是一件难事,只需要在AndroidManifest....
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, Request_Code);
AndroidSD卡读写权限会经常用到,但由于最近的几个版本对该部分一直在做相应的变动,所以在此做个总结,梳理一下。 主要的权限为:android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE下面就没个版本对SDCard权限的变化做详细的介绍:Android 4.4如果同时使用了机身存储
离上一次博客已经有快一年得时间了,我以为 TF卡已经退出历史舞台了,以前4.4. 5.0 只需要拿到文件夹路径,就可以直接对文件进行读写操作, 今天客户反馈7.1无法读写,有点郁闷,权限也是正常申请了,为啥还是读写有问题呢 ? 在博客里面找了半天,这个伙伴写到了点子上 :https://blog.csdn.net/qq_36467463/article/details/88691726 不信邪,钻们去下载了 ES文件浏览器,发现他有专门去申请一个权限,直接看源码吧,写起来也比较简单 权限申请,我用.
        在android开发中内存一般分为硬件设备自带的存储空间以及扩展存储空间(sdcard),本文只对开启sdcard的读写权限进行说明。 主要步骤分为两步          AndroidManifest.xml中添加权限说明 做项目遇到了一个棘手的问题,SD卡读写权限问题。1.android版本在6.0以上版本时,以下代码才有用:if (Build.VERSION.SDK_INT >= 23) { UiUtils.getInstance().showToast("1"); //减是否拥有权限checkCallPhonePermission != Packag
Android使用VFS(Virtual File System)虚拟文件系统。VFS提供了供存储设备挂载的节点。同一存储设备经过分区后,不同的分区可以挂在口上到不同的节点上,如手机的内置存储卡。 内置存储卡/外置sd卡 不等价内部存储/外部存储 不需要读写权限:内部存储Android 4.4以后的外部存储本应用的私有目录 需要权限Android4.4以后外部存储的公有目录和4.4之前的外部存储所有目录 本科学的写bug: 博主,您好,我今天试着跑了一下你的代码 ,dosbox显示 了错误,好像是 cmp dl,bx[si]处发生了错误,可能是cmp使用的语法错误,我在您的基础上进行了更改,https://blog.csdn.net/yuyanhaonan/article/details/127855707?spm=1001.2014.3001.5502 欢迎交流,同时感谢您所提供的帮助表情包表情包