Android不同存储方式与所需权限

首先,App在手机上保存文件或者缓存数据时,应该遵守以下几点:

1、不要随意占用用户的内置存储。
2、不要随意在SD卡上新建目录,应该放置自己应用包名对应的扩展存储目录下,卸载App时可以被自动清除。
3、对占用的磁盘空间有上限,并按照一定的策略进行清除。

一、手机存储路径

Android系统分为内部存储和外部存储,内部存储是手机系统自带的存储,一般空间都比较小,外部存储一般是SD卡的存储,空间一般都比较大,但不一定可用或者剩余空间可能不足。一般我们存储内容都会放在外部存储空间里。使用过程注意事项:

  • 先判断SD卡是否可用,可用时优先使用SD卡的存储,不可用时用内部存储
  • 存储在SD卡上时,可以在SD卡上新建任意一个目录存放,也可以存放在应用程序内部文件夹,区别是在SD卡的任意目录存放时内容不会随应用程序的卸载而消失,而在应用程序内部的内容会随应用程序卸载消失。
  • 一般缓存文件放在应用程序内部,用户主动保存的文件放在SD卡上的文件夹里。如果在SD卡上任意新建目录存放所有数据,用户卸载时会残存大量文件,招致用户反感。

一般我们可以通过 Context 和 Environment 相关的方法获取文件存取的路径。

1、内部存储

1)根目录
Environment.getDataDirectory(): /data

2)获取方式:

  • Context.getFileDir():获取内置存储下的文件目录,可以用来保存不能公开给其他应用的一些敏感数据如用户个人信息
  • Context.getCacheDir():获取内置存储下的缓存目录,可以用来保存一些缓存文件如图片,当内置存储的空间不足时将系统自动被清除

3)绝对路径

Environment.getDataDirectory(): /data //根目录
Context.getCacheDir(): /data/data/应用包名/cache
Context.getFilesDir(): /data/data/应用包名/files
Context.getFileStreamPath(""): /data/data/应用包名/files
Context.getFileStreamPath(“name”): /data/data/应用包名/files/name //name为子文件名

4)写权限:不需要申请

5)这是手机的内置存储,没有root的过的手机是无法用文件管理器之类的工具查看的。而且这些数据也会随着用户卸载App而被一起删除。这两个目录其实就对应着:
设置->应用->你的App->存储空间 下面的清除数据和清楚缓存。

2、外部存储1——应用扩展存储

1)根目录
Environment.getExternalStorageDirectory(): /storage/emulated/0

2)获取方式:

  • Context.getExternalFilesDir():获取SD卡上的文件目录
  • Context.getExternalCacheDir():获取SD卡上的缓存目录

3)绝对路径

Environment.getExternalStorageDirectory(): /storage/emulated/0 //根目录
Context.getExternalCacheDir(): /storage/emulated/0/Android/data/com.learn.test/cache
Context.getExternalFilesDir(""): /storage/emulated/0/Android/data/com.learn.test/files
Context.getExternalFilesDir(“test”): /storage/emulated/0/Android/data/com.learn.test/files/test
Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES): /storage/emulated/0/Android/data/com.learn.test/files/Pictures //type为文件类型

4)写权限:

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

API < 19:需要申请
API >= 19:不需要申请

从 API 19 / Andorid 4.4 / KITKAT 开始,不再需要显式声明这两个权限,除非要读写其他应用的应用数据($appDataDir)

5)既然是SD卡上的目录,那么是可以被其他的应用读取到的,所以这个目录下,不应该存放用户的敏感信息。同上面一样的,这里的文件会随着App卸载而被删除,也可以由用户手动在设置界面里面清除。

3、外部存储2——公共存储目录

1)获取方式

  • Environment.getExternalStorageDirectory()

2)绝对路径:SDCard/你设置的文件夹名字/

Environment.getExternalStorageDirectory(): /storage/emulated/0
Environment.getExternalStoragePublicDirectory(""): /storage/emulated/0
Environment.getExternalStoragePublicDirectory(“test”): /storage/emulated/0/test
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES): /storage/emulated/0/Pictures

3)写权限:需要申请

4)这些目录的内容不会随着应用的卸载而消失,如果我们的App需要存储一些公共的文件,甚至希望下载下来的文件即使在我们的App被删除之后,还可以被其他App使用,那么就可以使用这个目录。这个目录是始终需要申请SD写入权限的。

我们可以在外部存储上新建任意文件夹,不过在6.0及之后的系统需要动态申请权限

二、其他总结

1、基本操作

1)判断sd卡是否可用

/**
 * Check if the primary "external" storage device is available.
 */
public static boolean hasSDCardMounted() {
    String state = Environment.getExternalStorageState();
    if (state != null && state.equals(Environment.MEDIA_MOUNTED)) {
        return true;
    } else {
        return false;
    }
}

2)Android6.0以上动态申请权限

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String[] permissions = {
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            };
            if (checkPermission(act)) {
                act.requestPermissions(permissions, REQUEST_CODE_PERMISSION_STORAGE);
        } 
        
private static boolean checkPermission(Activity act) {
        return ((act.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
                || (act.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED));


3)一个完整的判断参考

if ((Build.VERSION.SDK_INT >= 19 || PermissionChecker.checkCallingOrSelfPermission(activity,
                        "android.permission.WRITE_EXTERNAL_STORAGE") == PermissionChecker.PERMISSION_GRANTED)
                        && Environment.getExternalStorageState().equals("mounted")
                        && external != null) {
                    sdCardPath = external.getAbsolutePath();
                }
2、Android6.0下应该把文件放到哪里

如果仅仅是做了简单的图片缓存工作,那么我们应该把图片缓存放到/data/data/应用包名/cache/或者SDCard/Android/data/应用包名/cache/,因为在6.0系统(API > 23)时,不需要申请权限就可以向这两个目录写入文件。而且/data/data/应用包名/cache/目录,是内置存储的应用私有缓存目录,在系统空间不够时还会被自动清除,对于图片缓存来讲也是一个不错的管理策略,不过谷歌建议我们最好还是自己实现缓存清除管理,例如用DiskLruCache。

实际上我们可以在API >= 19(不一定非要大于23)时,就可以在不需要申请权限的情况下把文件放到这两个目录了。如果开发的时候足够规范,即使在API < 19时,我们申请到写入权限后,我们也应该手动创建和前面相同的目录,使得应用存储数据目录统一化。

3、兼容API < 19

为了兼容API < 19,会在AndroidManifest.xml文件里注册WRITE_EXTERNAL_STORAGE,这时当App运行在一台6.0的设备时,即使你的App全程都没有调用requestPermissons来申请权限,用户还是可以在Android6.0系统上 进入设置->应用->你的App->权限里面,取消存储空间这一个权限。记住是运行在6.0系统的机器上,这是关键,因为低于6.0的系统根本没有这个设置。只要在manifest里面注册了,就可以动态取消之,此时你的图片在6.0机器上也就没法缓存喽。

解决:

将AndroidManifest.xml文件中的

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

改为

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

表示只在API <= 18时,才申请WRITE_EXTERNAL_STORAGE权限。这样用户就无法在Android6.0系统的设置下面看到存储空间权限的开关,当然也就无法关闭它了。

3、如果希望存储的内容可以被其他应用访问 不要用内部存储
4、分享存储文件时 Android N(7.0)注意

对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。
要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。

private static Uri getFileUri(Context context, File file, Intent intent) {
        Uri uri;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            uri = Uri.fromFile(file);
        } else {
            uri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }
        return uri;
    }

参考:
数据和文件存储概览

将文件保存到外部存储

Android存储及getCacheDir()、getFilesDir()、getExternalFilesDir()、getExternalCacheDir()区别

Android6.0权限适配之WRITE_EXTERNAL_STORAGE(SD卡写入)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章