精华内容
下载资源
问答
  • 今天小编就为大家分享一篇解决android6.0以上不能读取外部存储权限的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • Android 外部存储权限分析

    万次阅读 多人点赞 2014-05-24 19:56:26
    将文件等copy到手机中,则只能存储到内部存储器中,而无法存储到外置sdcard中,而且无法创建新的文件夹,这样一 来给用户和开发者都带来了一定的不便。之所以在KitKat之后版本中无法操作外置Sdcard,是因为Google...

    不知道你有么有发现,来自菜鸟的成长史:http://blog.csdn.net/zjbpku/article/details/25161131

    KitKat之后的版本不再支持用户对外置SDcard(Secondary Storage)的写入等操作。如果用户想要将文件等copy到手机中,则只能

    存储到内部存储器中,而无法存储到外置sdcard中,而且无法创建新的文件夹,这样一来给用户和开发者都带来了一定的不便。之所

    以在KitKat之后版本中无法操作外置Sdcard,是因为Google更改了此模块的权限,以前我们可以直接获取WRITE_EXTERNAL_STORAGE

    和READ_EXTERNAL_STORAGE权限来直接操作Sdcard,现在则不能,其目的是软件卸载时能将该软件创建的文件全部删除。据Google

    员工Jeff SharKey(此模块的开发者)介绍,自Kitkat之后Anroid提供了新的API去访问Secondary External Storage,但这不是本文重点,本

    文重点是分析外部存储权限是如何作用的。


    在KitKat之前的Android版本会给应用程序单独分出一块外部存储空间(external storage),这块存储空间可能在sdcard

    (可插拔的外置sdcaard)上,也可能在仅仅是在设备内部的闪存上,我们要获得WRITE_EXTERNAL_STORAGE权限在能对这块

    空间进行访问,如果只是读取内容则不需要权限。在4.4 KitKat及之后的版本中,Google做了两个变化:1、进行读取时需要

    READ_EXTERNAL_STORAGE权限;2、访问应用所属的目录下(如:android/data/[package name])存储的数据是不需要任

    何权限的。


    KitKat中,外部存储(external storage)被分割成了多个部分:一个“primary”部分,一个或多个“secondary”部分。在Kitkat之前的

    API 可以用来操作 primary external storage,secondary external storage 是对write权限做了稍微修改,与上边所述一样,在应用所

    所属的目录(如:android/data/[package name])下,对文件是有所有操作权限的,在应用所能管理到目录之外,该应用则不具有写

    的权限,不能进行任何写的操作。这里也就引出了本文的重点。ps:Google虽然没有要求各厂商在Sdcard的操作上添加额外权限,但

    是它却强制要求制造商对secondary external storage做了权限限制。如果你对Internal storage和external storage有疑问,可以看看文

    档  https://developer.android.com/guide/topics/data/data-storage.html#filesInternal


    根据Jeff SharKey 的介绍,当前版本的Android系统,也就是Kitkat,使用FUSE (Filesysgem in Userspace ) 对external storage进

    行管理。为了在文件创建时获取必要的权限,动态地接受或拒绝来自用户/组的个别请求,会有一个Android 守护进程参与与FUSE 内核

    驱动的交互。这仅仅是Android在FAT File System 格式化后的可移动卷上使用Linux型权限的一部分,在内核中它也允许使用超出基本的

    owner/gouper/user 执行的多级权限控制。看看下面Jeff Sharkey的解释:


    https://android.googlesource.com/platform/system/core/+/master/sdcard/sdcard.c


    在4.4之前,framework api对存储卷(storage volumes)的操作并没有很大的改变,设备制造商可以创建单个“primary”卷或者多个“secondary”

    卷,而这些不同的卷都能被系统服务StorageManager和MountService管理,这中情况下访问“primary”部分就像访问单个external storage一样。

    很多设备有Sd卡,但是都没有把它当作external storage,实际上这就是这些设备的“secondary volume”。例如,三星的Galaxy系列就是属于这

    一类,从权限方面来说,sd卡其实像外部存储卷一样被管理,但是作为设备的“secondary external storage",是没有API可以进行写的操作的。

    下面的这段代码来自AOSP device storage conf iguration example:

    on init
    	mkdir /mnt/shell/emulated 0700 shell shell
    	mkdir /storage/emulated 0555 root root
    	mkdir /mnt/media_rw/sdcard1 0700 media_rw media_rw
    	mkdir /storage/sdcard1 0700 root root
    	export EXTERNAL_STORAGE /storage/emulated/legacy
    	export EMULATED_STORAGE_SOURCE /mnt/shell/emulated
    	export EMULATED_STORAGE_TARGET /storage/emulated
    	export SECONDARY_STORAGE /storage/sdcard1
    系统内部的应用可以访问secondary storage的任何部分,对于第三方应用几乎不可能(目前 ES FileExploreAirdroidFx等几个文件应用通过

    特别的解决方法可以实现对某些机型外部存储文件的操作)。(关于如何在4.4上操作文件可以参考Storage Options。自4.4开始,Google引入

    SAF框架Storage Access Framework),如果Google以后不改变现在对4.4系统外置sd的操作权限,对于开发者而言,熟悉SAF框架也许是必要的。

    另,在4.4系统内部应用中,你会发现有一个叫DocumentUI的apk,这个就是用来处理SAF的一些接口的。)


    在external storage下的目录文件拥有相同的权限,如下:

    4.4 设备:

    root@generic:/storage/sdcard # ll
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Alarms
    d---rwxr-x system sdcard_rw 2014-05-06 13:21 Android
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 DCIM
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Download
    d---rwxr-x system sdcard_rw 2014-05-06 13:18 LOST.DIR
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Movies
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Music
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Notifications
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Pictures
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Podcasts
    d---rwxr-x system sdcard_rw 2014-05-06 13:20 Ringtones
    
    root@generic:/storage/sdcard # ll Android/data/
    drwxrwx--- system sdcard_rw 2014-05-06 13:21 com.google.android.apps.maps

    4.4 设备:

    root@generic:/storage/sdcard # ll
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Alarms
    drwxrwx--x root sdcard_r 2013-11-27 23:36 Android
    drwxrwx--- root sdcard_r 2014-05-06 01:33 DCIM
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Download
    drwxrwx--- root sdcard_r 2013-11-28 04:33 LOST.DIR
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Movies
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Music
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Notifications
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Pictures
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Podcasts
    drwxrwx--- root sdcard_r 2013-11-27 23:35 Ringtones
    
    root@generic:/storage/sdcard # ll Android/data/
    drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 com.google.android.apps.maps
    
    root@generic:/storage/sdcard # ll Android/data/com.google.android.apps.maps/
    drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 cache
    drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 testdata


    注意:在4.3中,sdcard_rw组有全部的读写权限,在Kitkat中,sdcard_r 组有 +rwx 所有权限,实际上这是明显不对的。并不等表示全部,

    因为Fuse守护进程会在运行时中积极地参与修改应用的权限。这对File APIs canWrite(),canRead()和canExecute()的执行结果有很大的影

    响,这些方法返回的值被单独地记录在内核文件系统中,所以他们都会返回true,即使试图以POSIX打开文件也会失败。(在4.4的外置sd

    卡上,是不能在文件夹写入一下文件的,但是当你试图调用canWrite()方法来判断该文件夹是否可写时,它仍会返回true值,所以此法不可取)


    android.permission.WRITE_EXTERNAL_STORAGE权限被授给sdcard_r组和sdcard_rw组的成员,但在kitkat中认证write权限需要一些动

    态的检查,因此FUSE守护进程会被用来补充文件系统的权限,FUSE守护进程会强制赋予拥有特定目录的App每个权限(也就是访问自身数

    据存储的目录android/data/pack-agename...及一些公共目录)。对于sdcard_rw组中使用-w标志配置的非默认所有者,FUSE守护进程也会强

    制赋予write-protected权限。

    service sdcard /system/bin/sdcard -u 1023 -g 1023 -l /data/media /mnt/shell/emulated
        class late_start
    
    service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1
        class late_start
        disabled

    从上面两句程序可以看到,FUSE守护进程强制控制GID 1023(media_rw,系统应用才有)才能对secondar storage进行写操作。再引入

    一个问题,在4.4中将external storage 分为primary和secondary,在primary部分(内置sdcard)是可以进行写操作的,而在secondary部分

    (外置sdcard)是不允许的,那FUSE Daemon是如何区分控制的呢?据Jeff 解释说: “-w 2013" 就表明了强制使用media_rw GID才能在

    secondary部分具有write权限。


    下面我们就梳理一下,如果在拥有外置sd卡的kitkat设备上进行文件操作,对于开发者而言哪些能做、哪些不能做?下图给出开发者会尝试

    的一些操作及结果:

                                              


    总结一下,自4.4开始Google对secondary volume做了限制之后,不仅为用户带来了不便,也为设备制造商及开发者带来了诸多不便,华为

    更是为此给开发者们发了一份通告:Android4.4上应用写外卡的兼容性问题与解决建议。如今,除了一些OEM厂商自行修改权限后的Rom对

    第三方应用没有限制外,大牛们也为已Root的设备用户提出修改platform.xml文件来修改权限(具体放法请百度之)以使第三方应用可以操作

    外置sd卡;还有一些上面提到的文件管理工具也可以操作外置sd卡。不管Google做限制的初衷是什么,希望Google从用户的角度来考虑问题,

    对Android系统做出更好的该进。在此感谢一下FX 文件管理工具的开发者Tod Liebeck 在G+对我问题的及时解答及帮助,同时也感谢一下给

    Tod Liebeck解决Kitkat外置sd文件操作方案的X-plore的开发者。





    展开全文
  • android外部存储读写权限问题

    万次阅读 2016-08-17 15:03:08
    我们知道,android分内部存储和外部存储,我们通常的理解就是除了sd卡的就是内部存储,而sd就是外部存储。现在大部分手机都没有sd卡,而是16G,8G什么的,其中并不是全部是内部存储,而是有一部分是像SD卡一样也是...

    今天在做一个实现应用内更新的模块,在把下载的apk文件存入外部存储是遇到一个问题,最后终于解决了。现将过程记录下来。
    我们知道,android分内部存储和外部存储,我们通常的理解就是除了sd卡的就是内部存储,而sd就是外部存储。现在大部分手机都没有sd卡,而是16G,8G什么的,其中并不是全部是内部存储,而是有一部分是像SD卡一样也是外部存储。
    我们的应用程序就是处于内部存储,他的路径是data/data/<包名>,如果我们想把下载的文件下载在这里,调用Context类的getFileDir()就可以了,但是这样的话,该apk处于应用内部,就无法被其他应用程序使用了,使用如下代码去执行安装时,就会失败了:

    /**
             * 安装新版本应用
             */
            private void installApp() throws  Exception{
                if(!file.exists()) {
                    return;
                }
                // 跳转到新版本应用安装页面
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.parse("file://" + file.getPath()),"application/vnd.android.package-archive");
                context.startActivity(intent);
            }

    所以我们需要下载到外部存储中,调用Context类的getExternalFilesDir()就可以了,另外需要在清单文件中添加权限:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18"/>
    这样确实下载成功了,但是之后在华为E887(可能记错,但没关系)中出错,提示没有权限,该系统为android4.4.4 , 4G内存,并且插有2G的SD卡,我就纳闷了,我不是加了权限么?后来查阅google文档发现该方法,在android4.4以下系统中某些设备中既有外部存储(4G内存中也有外部存储),又有sd卡作为外部储存时,该方法无法获取文件的绝对路径进行读写,
    可以调用Context类的getExternalFilesDirs()方法,该方法返回一个File数组,数组中的第一项被认为是主要外部存储,除非它不可用。

    file=new File(context.getExternalFilesDirs(Environment.DIRECTORY_DOWNLOADS)[0],"Test.apk");

    google原文如下:
    Sometimes, a device that has allocated a partition of the internal memory for use as the external storage may also offer an SD card slot. When such a device is running Android 4.3 and lower, the getExternalFilesDir() method provides access to only the internal partition and your app cannot read or write to the SD card. Beginning with Android 4.4, however, you can access both locations by calling getExternalFilesDirs(), which returns a File array with entries each location. The first entry in the array is considered the primary external storage and you should use that location unless it’s full or unavailable. If you’d like to access both possible locations while also supporting Android 4.3 and lower, use the support library’s static method, ContextCompat.getExternalFilesDirs(). This also returns a File array, but always includes only one entry on Android 4.3 and lower.

    另附一下下载完整代码

     /**
             * 下载新版本应用
             */
            private void downloadApp() {
                new Thread(new Runnable() {
                    @TargetApi(Build.VERSION_CODES.KITKAT)
                    @Override
                    public void run() {
                        URL url1 = null;
                        InputStream in = null;
                        FileOutputStream out = null;
                        HttpURLConnection conn = null;
                        try {
                            url1 = new URL(url);
                            conn = (HttpURLConnection) url1.openConnection();
                            conn.connect();
                            long fileLength = conn.getContentLength();
                            in = conn.getInputStream();
                            file=new File(context.getExternalFilesDirs(Environment.DIRECTORY_DOWNLOADS)[0],"Test.apk");
                            out = new FileOutputStream(file);
                            byte[] buffer = new byte[1024];
                            int len = 0;
                            long readedLength =0;
                            while((len = in.read(buffer)) != -1) {
                                // 用户点击“取消”按钮,下载中断
                                if(isCancel) {
                                    break;
                                }
                                out.write(buffer, 0, len);
                                readedLength += len;
                                curProgress = (int) (((float) readedLength / fileLength) * 100);
                                handler.sendEmptyMessage(UPDARE_TOKEN);
                                if(readedLength >= fileLength) {
                                    dialog.dismiss();
                                    // 下载完毕,通知安装
                                    handler.sendEmptyMessage(INSTALL_TOKEN);
                                    break;
                                }
                            }
                            out.flush();
                        } catch (Exception e) {
    
                            e.printStackTrace();
                        } finally {
                            if(out != null) {
                                try {
                                    out.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                            if(in != null) {
                                try {
                                    in.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                            if(conn != null) {
                                conn.disconnect();
                            }
                        }
                    }
                }).start();
            }
    展开全文
  • 上面不难看出,在manifest>中直接添加的读取和写入的权限 安卓6.0以上的版本怎么办,就需要动态申请权限了 下面添加了一个显示跳转与隐式跳转的两种方式,是activity组件的知识,不必在意在此处 package ...
  • 外部存储 sharedpreferences:link. 本文主要介绍外部存储的相关知识。 存储路径:/storage/emulated/0 ———————————————————————————————————————— 1、向SD卡写数据 步骤:...
  • Android Q中引入了分区储存功能,在外部存储设备中为每个应用提供了一个“隔离存储沙盒”。其他应用无法直接访问应用的沙盒文件。由于文件是应用的私有文件,不再需要任何权限即可访问和保存自己的文件。此变更并...
  • KitKat之后的版本不再支持用户对外置SDcard(Secondary Storage)的写入等操作。...存储到内部存储器中,而无法存储到外置sdcard中,而且无法创建新的文件夹,这样一来给用户和开发者都带来了一定的不便。之所

    转自:http://blog.csdn.net/zjbpku/article/details/25161131

    KitKat之后的版本不再支持用户对外置SDcard(Secondary Storage)的写入等操作。如果用户想要将文件等copy到手机中,则只能

    存储到内部存储器中,而无法存储到外置sdcard中,而且无法创建新的文件夹,这样一来给用户和开发者都带来了一定的不便。之所

    以在KitKat之后版本中无法操作外置Sdcard,是因为Google更改了此模块的权限,以前我们可以直接获取WRITE_EXTERNAL_STORAGE

    和READ_EXTERNAL_STORAGE权限来直接操作Sdcard,现在则不能,其目的是软件卸载时能将该软件创建的文件全部删除。据Google

    员工Jeff SharKey(此模块的开发者)介绍,自Kitkat之后Anroid提供了新的API去访问Secondary External Storage,但这不是本文重点,本

    文重点是分析外部存储权限是如何作用的。


    在KitKat之前的Android版本会给应用程序单独分出一块外部存储空间(external storage),这块存储空间可能在sdcard

    (可插拔的外置sdcaard)上,也可能在仅仅是在设备内部的闪存上,我们要获得WRITE_EXTERNAL_STORAGE权限在能对这块

    空间进行访问,如果只是读取内容则不需要权限。在4.4 KitKat及之后的版本中,Google做了两个变化:1、进行读取时需要

    READ_EXTERNAL_STORAGE权限;2、访问应用所属的目录下(如:android/data/[package name])存储的数据是不需要任

    何权限的。


    KitKat中,外部存储(external storage)被分割成了多个部分:一个“primary”部分,一个或多个“secondary”部分。在Kitkat之前的

    API 可以用来操作 primary external storage,secondary external storage 是对write权限做了稍微修改,与上边所述一样,在应用所

    所属的目录(如:android/data/[package name])下,对文件是有所有操作权限的,在应用所能管理到目录之外,该应用则不具有写

    的权限,不能进行任何写的操作。这里也就引出了本文的重点。ps:Google虽然没有要求各厂商在Sdcard的操作上添加额外权限,但

    是它却强制要求制造商对secondary external storage做了权限限制。如果你对Internal storage和external storage有疑问,可以看看文

    档  https://developer.android.com/guide/topics/data/data-storage.html#filesInternal


    根据Jeff SharKey 的介绍,当前版本的Android系统,也就是Kitkat,使用FUSE (Filesysgem in Userspace ) 对external storage进

    行管理。为了在文件创建时获取必要的权限,动态地接受或拒绝来自用户/组的个别请求,会有一个Android 守护进程参与与FUSE 内核

    驱动的交互。这仅仅是Android在FAT File System 格式化后的可移动卷上使用Linux型权限的一部分,在内核中它也允许使用超出基本的

    owner/gouper/user 执行的多级权限控制。看看下面Jeff Sharkey的解释:


    https://android.googlesource.com/platform/system/core/+/master/sdcard/sdcard.c


    在4.4之前,framework api对存储卷(storage volumes)的操作并没有很大的改变,设备制造商可以创建单个“primary”卷或者多个“secondary”

    卷,而这些不同的卷都能被系统服务StorageManager和MountService管理,这中情况下访问“primary”部分就像访问单个external storage一样。

    很多设备有Sd卡,但是都没有把它当作external storage,实际上这就是这些设备的“secondary volume”。例如,三星的Galaxy系列就是属于这

    一类,从权限方面来说,sd卡其实像外部存储卷一样被管理,但是作为设备的“secondary external storage",是没有API可以进行写的操作的。

    下面的这段代码来自AOSP device storage conf iguration example:

    [java]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. on init  
    2.     mkdir /mnt/shell/emulated 0700 shell shell  
    3.     mkdir /storage/emulated 0555 root root  
    4.     mkdir /mnt/media_rw/sdcard1 0700 media_rw media_rw  
    5.     mkdir /storage/sdcard1 0700 root root  
    6.     export EXTERNAL_STORAGE /storage/emulated/legacy  
    7.     export EMULATED_STORAGE_SOURCE /mnt/shell/emulated  
    8.     export EMULATED_STORAGE_TARGET /storage/emulated  
    9.     export SECONDARY_STORAGE /storage/sdcard1  
    系统内部的应用可以访问secondary storage的任何部分,对于第三方应用几乎不可能(目前 ES FileExplore Airdroid Fx 等几个文件应用通过

    特别的解决方法可以实现对某些机型外部存储文件的操作)。(关于如何在4.4上操作文件可以参考Storage Options。自4.4开始,Google引入

    SAF框架Storage Access Framework),如果Google以后不改变现在对4.4系统外置sd的操作权限,对于开发者而言,熟悉SAF框架也许是必要的。

    另,在4.4系统内部应用中,你会发现有一个叫DocumentUI的apk,这个就是用来处理SAF的一些接口的。)


    在external storage下的目录文件拥有相同的权限,如下:

    4.3 设备:

    [java]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. root@generic:/storage/sdcard # ll  
    2. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Alarms  
    3. d---rwxr-x system sdcard_rw 2014-05-06 13:21 Android  
    4. d---rwxr-x system sdcard_rw 2014-05-06 13:20 DCIM  
    5. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Download  
    6. d---rwxr-x system sdcard_rw 2014-05-06 13:18 LOST.DIR  
    7. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Movies  
    8. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Music  
    9. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Notifications  
    10. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Pictures  
    11. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Podcasts  
    12. d---rwxr-x system sdcard_rw 2014-05-06 13:20 Ringtones  
    13.   
    14. root@generic:/storage/sdcard # ll Android/data/  
    15. drwxrwx--- system sdcard_rw 2014-05-06 13:21 com.google.android.apps.maps  

    4.4 设备:

    [java]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. root@generic:/storage/sdcard # ll  
    2. drwxrwx--- root sdcard_r 2013-11-27 23:35 Alarms  
    3. drwxrwx--x root sdcard_r 2013-11-27 23:36 Android  
    4. drwxrwx--- root sdcard_r 2014-05-06 01:33 DCIM  
    5. drwxrwx--- root sdcard_r 2013-11-27 23:35 Download  
    6. drwxrwx--- root sdcard_r 2013-11-28 04:33 LOST.DIR  
    7. drwxrwx--- root sdcard_r 2013-11-27 23:35 Movies  
    8. drwxrwx--- root sdcard_r 2013-11-27 23:35 Music  
    9. drwxrwx--- root sdcard_r 2013-11-27 23:35 Notifications  
    10. drwxrwx--- root sdcard_r 2013-11-27 23:35 Pictures  
    11. drwxrwx--- root sdcard_r 2013-11-27 23:35 Podcasts  
    12. drwxrwx--- root sdcard_r 2013-11-27 23:35 Ringtones  
    13.   
    14. root@generic:/storage/sdcard # ll Android/data/  
    15. drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 com.google.android.apps.maps  
    16.   
    17. root@generic:/storage/sdcard # ll Android/data/com.google.android.apps.maps/  
    18. drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 cache  
    19. drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 testdata  


    注意:在4.3中,sdcard_rw组有全部的读写权限,在Kitkat中,sdcard_r 组有 +rwx 所有权限,实际上这是明显不对的。并不等表示全部,

    因为Fuse守护进程会在运行时中积极地参与修改应用的权限。这对File APIs canWrite(),canRead()和canExecute()的执行结果有很大的影

    响,这些方法返回的值被单独地记录在内核文件系统中,所以他们都会返回true,即使试图以POSIX打开文件也会失败。(在4.4的外置sd

    卡上,是不能在文件夹写入一下文件的,但是当你试图调用canWrite()方法来判断该文件夹是否可写时,它仍会返回true值,所以此法不可取)


    android.permission.WRITE_EXTERNAL_STORAGE权限被授给sdcard_r组和sdcard_rw组的成员,但在kitkat中认证write权限需要一些动

    态的检查,因此FUSE守护进程会被用来补充文件系统的权限,FUSE守护进程会强制赋予拥有特定目录的App每个权限(也就是访问自身数

    据存储的目录android/data/pack-agename...及一些公共目录)。对于sdcard_rw组中使用-w标志配置的非默认所有者,FUSE守护进程也会强

    制赋予write-protected权限。

    [java]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. service sdcard /system/bin/sdcard -u 1023 -g 1023 -l /data/media /mnt/shell/emulated  
    2.     class late_start  
    3.   
    4. service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1  
    5.     class late_start  
    6.     disabled  

    从上面两句程序可以看到,FUSE守护进程强制控制GID 1023(media_rw,系统应用才有)才能对secondar storage进行写操作。再引入

    一个问题,在4.4中将external storage 分为primary和secondary,在primary部分(内置sdcard)是可以进行写操作的,而在secondary部分

    (外置sdcard)是不允许的,那FUSE Daemon是如何区分控制的呢?据Jeff 解释说: “-w 2013" 就表明了强制使用media_rw GID才能在

    secondary部分具有write权限。


    下面我们就梳理一下,如果在拥有外置sd卡的kitkat设备上进行文件操作,对于开发者而言哪些能做、哪些不能做?下图给出开发者会尝试

    的一些操作及结果:

                                              


    总结一下,自4.4开始Google对secondary volume做了限制之后,不仅为用户带来了不便,也为设备制造商及开发者带来了诸多不便,华为

    更是为此给开发者们发了一份通告:Android4.4上应用写外卡的兼容性问题与解决建议。如今,除了一些OEM厂商自行修改权限后的Rom对

    第三方应用没有限制外,大牛们也为已Root的设备用户提出修改platform.xml文件来修改权限(具体放法请百度之)以使第三方应用可以操作

    外置sd卡;还有一些上面提到的文件管理工具也可以操作外置sd卡。不管Google做限制的初衷是什么,希望Google从用户的角度来考虑问题,

    对Android系统做出更好的该进。在此感谢一下FX 文件管理工具的开发者Tod Liebeck 在G+对我问题的及时解答及帮助,同时也感谢一下给

    Tod Liebeck解决Kitkat外置sd文件操作方案的X-plore的开发者。


    展开全文
  • Android 11 存储权限适配指南

    千次阅读 2021-04-05 22:17:29
    而我们今天要讲的主题,是关于存储权限,在 Android 6.0 之后就变成了危险权限,而到了 Android 11 上面变成了特殊权限,而最明显的区别是一个是通过 Dialog 展示给用户看,另外一个是通过 Activity 展现给用户看。...

    Android权限大致可分为三类:

    • 普通权限:只需要在清单文件中注册即可
    • 危险权限:需要在代码中动态申请,以弹系统 Dialog 的形式进行请求
    • 特殊权限:需要在代码中动态申请,以跳系统 Activity 的形式进行请求

    而我们今天要讲的主题,是关于存储权限,在 Android 6.0 之后就变成了危险权限,而到了 Android 11 上面变成了特殊权限,而最明显的区别是一个是通过 Dialog 展示给用户看,另外一个是通过 Activity 展现给用户看。

    6.0权限
    6.0之后的危险权限

    11权限
    Android 11®之后的特殊权限

    Android 10.0 以下存储权限适配

    (1)升级 targetSdkVersion

    android 
        defaultConfig {
            targetSdkVersion 23
        }
    }
    

    (2)添加清单权限

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

    (3)代码动态申请

    public final class PermissionActivity extends AppCompatActivity {
    
        private static final int REQUEST_CODE = 1024;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestPermission();
        }
    
        private void requestPermission() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // 先判断有没有权限
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                        ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                    writeFile();
                } else {
                    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
                }
            } else {
                writeFile();
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            if (requestCode == REQUEST_CODE) {
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                        ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                    writeFile();
                } else {
                    ToastUtils.show("存储权限获取失败");
                }
            }
        }
    
        /**
         * 模拟文件写入
         */
        private void writeFile() {
            ToastUtils.show("写入文件成功");
        }
    }
    

    (4)需要注意的是,如果 targetSdkVersion >= 29 上,还需要在清单文件中加上

    <application
        android:requestLegacyExternalStorage="true">
    

    否则就算申请了存储权限,在安卓 10.0 的设备上将无法正常读写外部存储上的文件

    Android 11 及以上申请存储权限

    (1)升级 targetSdkVersion

    android 
        defaultConfig {
            targetSdkVersion 30
        }
    }
    

    (2)添加清单权限

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

    (3)代码动态申请

    public final class PermissionActivity extends AppCompatActivity {
    
        private static final int REQUEST_CODE = 1024;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestPermission();
        }
    
        private void requestPermission() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                // 先判断有没有权限
                if (Environment.isExternalStorageManager()) {
                    writeFile();
                } else {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
                    intent.setData(Uri.parse("package:" + context.getPackageName()));
                    startActivityForResult(intent, REQUEST_CODE);
                }
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // 先判断有没有权限
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                        ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                    writeFile();
                } else {
                    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
                }
            } else {
                writeFile();
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            if (requestCode == REQUEST_CODE) {
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                        ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                    writeFile();
                } else {
                    ToastUtils.show("存储权限获取失败");
                }
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == REQUEST_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                if (Environment.isExternalStorageManager()) {
                    writeFile();
                } else {
                    ToastUtils.show("存储权限获取失败");
                }
            }
        }
    
        /**
         * 模拟文件写入
         */
        private void writeFile() {
            ToastUtils.show("写入文件成功");
        }
    }
    

    使用三方Jar来简单申请权限

    XXPermissions.with(this)
            // 不适配 Android 11 可以这样写
            //.permission(Permission.Group.STORAGE)
            // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
            .permission(Permission.MANAGE_EXTERNAL_STORAGE)
            .request(new OnPermissionCallback() {
    
                @Override
                public void onGranted(List<String> permissions, boolean all) {
                    if (all) {
                        toast("获取存储权限成功");
                    }
                }
    
                @Override
                public void onDenied(List<String> permissions, boolean never) {
                    if (never) {
                        toast("被永久拒绝授权,请手动授予存储权限");
                        // 如果是被永久拒绝就跳转到应用权限系统设置页面
                        XXPermissions.startPermissionActivity(MainActivity.this, permissions);
                    } else {
                        toast("获取存储权限失败");
                    }
                }
            });
    
    展开全文
  • android,内部存储,外部存储,权限理解

    千次阅读 2017-07-26 09:59:43
    这是我的一些理解:首先android 的文件存储有两大类,第一个是Context (内部存储),第二个是Environment类 (外部存储) ----------------------------------内部存储----START---------------------------------------...
  • 而我们今天要讲的主题,是关于存储权限,在 Android 6.0 之后就变成了危险权限,而到了 Android 11 上面变成了特殊权限,而最明显的区别是一个是通过 Dialog 展示给用户看,另外一个是通过 Activity 展
  • SharePreferences 存储 中,我们已经讲了 Sp 是如何保存数据的,并且也提供了一个 Sp 的工具类,今天我们来讲解一下 Android 中本地存储数据的另外一种方式——文件存储,文件存储又分为外部存储和内部存储。...
  • 外部存储适配方案3.1 Android 10.0 以下外部存储权限适配3.2 Android 11 及以上申请外部存储权限4.权限申请框架推荐4.1 [XXPermissions](https://github.com/getActivity/XXPermissions)4.1 [AndPermission](https:/
  • Android使用VFS(Virtual File System)虚拟文件系统。VFS提供了供存储设备挂载的节点...需要权限Android4.4以后外部存储的公有目录和4.4之前的外部存储所有目录 参考:https://www.jianshu.com/p/23b20...
  • 一般的Android App能读取的存储空间主要有三种: app自己的私有目录,也就是/data/data/《app 目录》。 读写这个目录不需要单独的权限。每个app只能读写自己的目录,而不能读写其他app的目录。 Android通过Seandroid...
  • Android 11 外部存储权限适配指南及方案.pdf
  • 添加存储权限 <!--外部存储的写权限--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!--外部存储的读权限--><uses-permission android:name="android....
  • Android的世界中,应用程序可以使用的文件存储区域包括两个:内部存储空间、外部存储空间。这两个名称是在Android早期确定的,那时候大部分设备都提供内置的非易失性存储(内部存储空间)以及可移动的存储媒介,...
  • 对于很多做Android开发的同学来说,可能认为文件存储很简单,调用一些诸如getFilesDir,getExternalStorageDirectory方法行了,但是虽然说它们会调用相应的方法来实现简单的数据存储。但是他们未必就搞懂了他的数据...
  • uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <application .... android:...
  • Android不同存储方式与所需权限

    千次阅读 2020-02-24 16:24:56
    首先,App在手机上保存文件或者缓存数据时,应该遵守以下几点: 1、不要随意占用用户的...Android系统分为内部存储和外部存储,内部存储是手机系统自带的存储,一般空间都比较小,外部存储一般是SD卡的存储,空间一...
  • 目前最新是 Android Q Beta 5 版本。 Android Q 隐私权变更:分区存储 ...注:界面右上角可以切换成中文。...访问外部存储设备中的文件 从 Android Q 测试版 5 开始,以 Android 9(API 级别 28)或更低版本...
  • Android Q中引入了分区储存功能,在外部存储设备中为每个应用提供了一个“隔离存储沙盒”。 其他应用无法直接访问应用的沙盒文件。由于文件是应用的私有文件,不再需要任何权限即可访问和保存自己的文件。 此变更...
  • Android 4.4系统中,外置存储卡(SD卡)被称为二级外部存储设备(secondary storage),应用程序已无法往外置存储卡(SD卡)写入数据,并且WRITE_EXTERNAL_STORAGE只为设备上的主要外部存储(primary storage)授予写...
  • Android设备支持外部存储,比如SD卡等,保存在外部存储的数据具有全局可读性,可供在其他设备比如电脑上阅读,修改等。注意:使用外部存储需要获取外部存储的访问权限。本篇...
  • 存储特征 将数据存储在app目录下,保存到外部存储设备,比如SD卡中,数据既可以公有,也可以私有,空间相对充足。 存储路径 ...访问外部存储权限,API 23以上的应用程序,需要动态申请 外部存储权限...
  • Android存储权限之深入浅出

    千次阅读 2018-10-09 16:20:29
    Android文件存储权限解析 Android中常见的存储方式有SharedPreferences、Sqlite、文件IO存储、云存储、ContentProvider等。 其中IO存储最为灵活,适用场景也多。这里就不同文件路径和其对应的权限进行分析。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,821
精华内容 8,728
关键字:

android外部存储权限