Android存储权限梳理及api接口调用
背景
Android开发中需要了解android 存储权限管理和对应的api使用逻辑。
概述
Android系统的文件存储按存储介质类型分为内部存储和外部存储,按存储目录类型分为私有目录和公共目录;对于Android系统中的进程来说,如果需要存储空间,需要按需申请对应的权限才能访问;否则是不能达到目的的。
Android文件存储分类及接口
文件操作的各种API接口及继承关系如下:
内部路径 不用权限
内部存储
-
是否需要用户权限:否
-
是否能被气筒应用访问:否
-
卸载应用数据是否被删除:是
内部存储控件不需要用户权限,这意味着不需要用户去授权android.permission.WRITE_EXTERNAL_STORAGE
和android.permission.READ_EXTERNAL_STORAGE
就可以读写该目录下的文件。而且该目录下的文件不能被其他应用访问。这也就保证了应用内部存储的文件的安全性和隐私性,如果需要查看应用内部的文件,可以通过Android Studio的Device File Explore工具进行访问:
通过访问/data/data/应用包名,查看应用内部的存储文件
/data/data/应用名/cache :存放的是APP的缓存信息
/data/data/应用名/code_cache :在运行时存放应用产生的编译或者优化的代码
/data/data/应用名/files : 存放APP的文件信息
还有一些运行时,产生的文件夹,例如调用SharedPreference所产生的 /data/data/应用包名/shared_prefs 目录,存放着app的SharedPreference所产生的xml文件,还有调用数据库所产生的 **/data/data/应用包名/databases/**文件夹,这里就不一一举例。
相关API Context 类
getCacheDir
缓存路径
File getCacheDir() 返回内部存储的cache文件夹
Log.d("THINK",getCacheDir().getPath()); //data/user/0/com.example.camera/cache
getCodeCacheDir
File getCodeCacheDir() 返回内部存储的code_cache文件夹,要求Android5.0+
Log.d("THINK",getCodeCacheDir().getPath()); ///data/user/0/com.example.camera/code_cache
示意路径
getFilesDir
File getFilesDir() 访问内部存储的files目录
Log.d("THINK",getFilesDir().getPath()); ///data/user/0/com.example.camera/files
内部文件 文件路径
fileList
files 下的所有文件名
String[] fileList() 返回files目录下的文件名,例如files目录下有test.txt文件和test1.txt文件
//输出 test.txt和test1.txt
for (String item : fileList()){
Log.d("THINK",item);
}
相关的openFileInput和openFileOutput就是直接在files文件夹读写某个文件:
openFileOutput
FileOutputStream openFileOutput() 写文件到files目录下:
try {
FileOutputStream outputStream = openFileOutput("test.txt", Context.MODE_PRIVATE);
outputStream.write("this is test content".getBytes());
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
此时file目录下就会多了一个test.txt文件
openFileInput
FileInputStream openFileInput() 读files目录下文件,例如我们读刚刚写入的test.txt文件
// 输出:this is test content
try {
FileInputStream fileInputStream = openFileInput("test.txt");
byte[] bytes = new byte[1024];
StringBuffer stringBuffer = new StringBuffer();
int len = 0;
while ((len=fileInputStream.read(bytes))!=-1){
stringBuffer.append(new String(bytes,0,len));
}
Log.d("THINK",stringBuffer.toString());
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
getDataDir
不常用
File getDataDir() 返回内部存储的根文件夹,要求Android7.0+
Log.d("THINK",getDataDir().getPath()); ///data/user/0/com.example.camera
外部存储 公共目录
-
是否需要用户权限:是
-
是否能被其他应用访问:是
-
卸载应用数据是否被删除:否
公共目录 Environment 类 - 需要权限
需要在manifest xml 配置和request Permission
公共目录必须需要用户授权读写的权限,这意味着我们需要在AndroidManifest.xml
中注册用户权限。
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
并且在Android 6.0系统之后需要申请用户权限,并获得用户授权,才能读写文件。
公共目录相对开放,我们可以访问其他APP存在公共目录下的文件,并且当APP被删除时,并不会删除应用存在公共目录下的文件。
我们可以通过Environment对象,访问读写公共目录的文件。接下来看看相关的API。
相关API Environment类
Environment类提供了丰富的访问对应公有目录下的type,可以通过Environment源码进行查看。
Environment.getExternalStorageDirectory
File Environment.getExternalStorageDirectory() 访问外部存储设备公共根目录:
//输出:/storage/emulated/0
Log.d("THINK",Environment.getExternalStorageDirectory().getPath());
注意这个不能保证该接口有效,得调用上述的state 看看状态。获取的路径就是如下:
Environment.getExternalStorageState
String Environment.getExternalStorageState() 获得外部存储SD卡的状态,在读写SD卡的时候,应该先判断一下SD卡的状态,是否能够支持读写。
具体返回值及对应的状态也可以查看Environment.getExternalStorageState的源码。
Environment.getExternalStoragePublicDirectory(String type)
具体的对应文件夹
例如:
//输出:/storage/emulated/0/Music
Log.d("THINK",Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getPath();
外部存储 私有目录 Context类 - 不需要权限
-
是否需要用户权限:4.4以上不需要
-
是否能被其他应用访问:否
-
卸载应用数据是否被删除:是
私有目录,在Android4.4系统以上。不需要注册和用户授权SD读写的权限,就可以在应用的私有目录进行读写文件。并且文件不能被其他应用所访问,具有较好的隐私性和安全性,并且在用户删除的时候,对应的应用私有目录也会被删除。
私有目录地址:/storage/emulated/0/Android/data/应用包名
相关API
私有目录下访问的API都在ContextWrapper对象上。这意味这我们可以直接在Activity中调用或者通过Context对象去调用。
getExternalCacheDir
File Context.getExternalCacheDir() 访问/storage/emulated/0/Android/data/应用包名/cache目录,该目录用来存放应用的缓存文件,当我们通过应用删除缓存文件的时候,该目录下的文件会被清除。
//输出:/storage/emulated/0/Android/data/com.example.camera/cache
Log.d("THINK",this.getExternalCacheDir().getPath());
getExternalFilesDir
File Context.getExternalFilesDir(String type) 访问/storage/emulated/0/Android/data/应用包名/files 目录,该目录用来存放应用的数据,当我们通过应用去清除应用数据的时候,该目录下的文件会被清除。我们也可以通过type,去创建并获得对应目录下的File文件。例如:
//创建并输出:/storage/emulated/0/Android/data/com.example.camera/files/test
Log.d("THINK",this.getExternalFilesDir("test").getPath());
File file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsoluteFile();
使用方式和内部存储的差不多,不过路径不一样罢了。
也可以将type传为null,这时候访问的目录就是files目录,例如:
//输出:/storage/emulated/0/Android/data/com.example.camera/files
Log.d("THINK",this.getExternalFilesDir(null).getPath());
注意版本权限
·WRITE_EXTERNAL_STORAGE在android11 之后就不再支持了。。
高版本中使用存储权限可以 用如下的配置:
READ_MEDIA_AUDIO,
READ_MEDIA_IMAGES,
READ_MEDIA_VIDEO
外部存储权限的实现可以参考之前的文档:
Android R及以上版本中APP外部存储实现_tools:ignore="selectedphotoaccess-CSDN博客
总结
开发中经常会进行文件存储,可以根据具体需求,选择合适的方案对文件进行存储,参考如下:
-
当需要存储一些私密性和安全性比较高的小数据,例如用户的账号密码等信息,可以存放在内部存储空间上。
-
当需要存放一些能够被其他应用也能访问的数据,并且当应用被删除的时候,不希望数据被清除的时候,可以放到外部存储的公有目录下,但别忘了申请读写SD卡的权限。
-
当需要存放一些应用的数据和缓存数据,希望有较好的隐私性和应用被删除的时候,对应的应用数据和缓存也被删除,这时候可以选择存放在外部存储的私有目录下。