【Android-File】Android文件的读写
补充:https://blog.csdn.net/wxz1179503422/article/details/84874171 | Android FileProvider详细解析和踩坑指南
---------------------------------------
参考:https://blog.csdn.net/yoryky/article/details/78675373 | Android中的文件读写操作
参考:https://blog.csdn.net/baidu_17508977/article/details/51007904 | Android常见文件路径介绍
参考:https://blog.csdn.net/mad1989/article/details/37568667 | Android SD卡简单的文件读写操作
参考:https://blog.csdn.net/zadarrien_china/article/details/55226068 | Android 文件的读取和写入
参考:https://blog.csdn.net/weixin_39209728/article/details/79729885 | Linux中drwxr-xr-x.的意思和权限
参考:https://blog.csdn.net/mylf19/article/details/74092011 | Android studio中关于模拟器/data目录不能显示的解决办法
注:本文不严谨。
目录
1 权限
2 获取存储路径
2.1 获取应用内部存储区路径方法 - Internal
2.2 获取应用外部存储区路径方法 - External
2.2.1 区域A:使用Context获取package_name相关路径
2.2.2 区域B:使用Environment类获取外部路径
3 读写内外存储空间的文件
3.1 读写App内部存储空间的文件
3.1.1 读文件 - Internal
3.1.2 写文件 - Internal
3.2 读写App外部存储空间的文件
3.2.1 读文件 - External
3.2.2 写文件 - External
4 验证读写功能及权限
4.1 读写Internal文件
4.1.1 写入Internal文件
4.1.2 读出Internal文件
4.2 读写External包名区文件
4.2.1 写入External包名区文件
4.2.2 读出External包名区文件
4.3 读写External Storage自由区的文件
4.3.1 向External Storage自由区写入文件
4.3.2 从External Storage自由区读出文件
1 权限
2 获取存储路径
android可能有两个地方可以存储文件,相对于应用程序来说,可分为内部和外部两个位置,外部又分为私有和公共两个区域:
存储位置 | 解释 | 文件管理器 | 访问权限 | 应用卸载后 | 外应用访问 | 其他别称 |
Internal | 应用安装存储区 | 不可见 | 不需权限 | 文件丢失 | 不可访问 | |
External_Package | 外部私有包名区 | 可见 | 不需权限 | 文件丢失 | 不可访问 | 同下统称 |
External_Storage | 外部公共自由区 | 可见 | READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE | 文件留存 | 可以访问 | 存储盘/SD卡/ 内部存储设备 |
Internal storage:
总是可用的
这里的文件默认只能被我们的app所访问。
当用户卸载app的时候,系统会把internal内该app相关的文件都清除干净。
Internal是我们在想确保不被用户与其他app所访问的最佳存储区域。
External storage:
并不总是可用的,因为用户有时会通过USB存储模式挂载外部存储器,当取下挂载的这部分后,就无法对其进行访问了。
是大家都可以访问的,因此保存在这里的文件可能被其他程序访问
当用户卸载您的应用时,只有通过 getExternalFilesDir() 将您的应用的文件保存在目录中时,系统才会从此处删除您的应用的文件
External是在不需要严格的访问权限并且希望这些文件能够被其他app共享或是允许用户通过电脑访问时的最佳存储区域。
2.1 获取应用内部存储区路径方法 - Internal
获取应用内部存储区路径只能通过Context来获取,不需要额外敏感权限
// 位置【data/data//cache】String pathCache = getCacheDir().getAbsolutePath();// 位置【data/data//files】String pathFile = getFilesDir().getAbsolutePath();
其真实目录为:
根目录\data\data\
\cache // getCacheDir().getAbsolutePath();方法获取到的
根目录\data\data\\files // getFilesDir().getAbsolutePath();方法获取到的 根目录\data\data\
\database // 数据库存储位置
根目录\data\data\\shared_prefs // SharedPreferences存储目录
打开Android Studio右下角的 Device File Explorer 可以看到手机存储的目录结构
获取到的文件所在的存储路径是App内部存储(不是“外部存储空间”),因此对于用户通过手机的文件管理是看不到该目录的:
2.2 获取应用外部存储区路径方法 - External
获取应用外部存储区路径需要声明读取外部存储区的读写权限
获取应用外部存储区有两种途径:
①使用Context获取package_name相关路径; // 应用卸载后,文件被删除
②使用Environment类获取package_name无关路径。 // 应用卸载后,文件仍存在
2.2.1 区域A:使用Context获取package_name相关路径
获取Cache路径的方法:
// 真实位置【/mnt/sdcard/Android/data//cache】// 模拟位置【/storage/emulated/0/Android/data//cache】String pathExternalCache = getExternalCacheDir().getAbsolutePath();
获取到的目录为:
真实位置:根目录\mnt\sdcard\Android/data\
\cache
模拟位置:设备/storage/emulated/0/Android/data//cache
获取Files路径的方法:
// 真实位置【/mnt/sdcard/Android/data//files/Movies】// 模拟位置【/storage/emulated/0/Android/data//files/Movies】String pathExternalFiles = getExternalFilesDir(Environment.DIRECTORY_MOVIES).getAbsolutePath();
获取到的目录:
真实位置:根目录\mnt\sdcard\Android/data\
\files\具体目录
模拟位置:设备/storage/emulated/0/Android/data//files/具体目录
getExternalFilesDir(String type);方法中可以传入Environment的type参数,可用于指定其具体目录。
此方法获取目录路径的文件目录如下,因位于App外部存储空间,是可以在手机自带的 “文件管理” 中看到的:
左图是在设备中的真实路径,右图为通过手机“文件管理”看到的目录。此途径应用卸载后存储在该目录下的文件会被清除掉
2.2.2 区域B:使用Environment类获取外部路径
方式①:获取公共目录
// 真实位置【/mnt/sdcard/Pictures】// 模拟位置【/storage/emulated/0/Pictures】String pathExternalPublic = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath();
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_XXX).getAbsolutePath();
真实位置:根目录\mnt\sdcard\具体公共目录
模拟位置:设备/storage/emulated/0/具体公共目录
其中,获取公共目录的Environment的Type参数有:
Environment的Type参数 | 对应模拟路径 | 解释说明 |
DIRECTORY_MOVIES | /storage/emulated/0/Movies | 电影 |
DIRECTORY_DCIM | /storage/emulated/0/DCIM | 相册 |
DIRECTORY_ALARMS | /storage/emulated/0/Alarms | 铃音 |
DIRECTORY_DOCUMENTS | /storage/emulated/0/Documents | 文件 |
DIRECTORY_DOWNLOADS | /storage/emulated/0/Download | 下载文件 |
DIRECTORY_MUSIC | /storage/emulated/0/Music | 音乐 |
DIRECTORY_NOTIFICATIONS | /storage/emulated/0/Notifications | 通知 |
DIRECTORY_PICTURES | /storage/emulated/0/Pictures | 图片 |
DIRECTORY_PODCASTS | /storage/emulated/0/Podcasts | 播客 |
DIRECTORY_RINGTONES | /storage/emulated/0/Ringtons | 铃声 |
方式②:获取自由目录
// 真实位置【/mnt/sdcard】// 模拟位置【/storage/emulated/0】String pathExternalRoot = Environment.getExternalStorageDirectory().getAbsolutePath();// 真实位置【/mnt/sdcard/DCIM】// 模拟位置【/storage/emulated/0/DCIM】String pathExternalDcim = Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM";// 真实位置【/mnt/sdcard/DCIM/Camera/test.jpg】// 模拟位置【/storage/emulated/0/DCIM/Camera/test.jpg】String pathExternalTest = Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/test.jpg";
目录:
Environment.getExternalStorageDirectory().getAbsolutePath();
真实位置:根目录\mnt\sdcard
模拟位置:设备/storage/emulated/0/Environment.getExternalStorageDirectory().getAbsolutePath() + "/xxx/xxx/xxx.txt";
真实位置:根目录\mnt\sdcard\具体目录
模拟位置:设备/storage/emulated/0/具体目录
看一下目录结构:
这也便是我们熟悉的 “文件管理” 界面(右图)展示的目录的真实结构(左图)
这个目录下为即为所谓的相对于App应用的“外部存储空间”。
为什么右图写的是“内部存储设备”呢?这是因为分类标准不同,因现在手机机身存储都集成在手机之内,相对于外部可插拔的SD卡而言,机身存储即为“内部存储设备”。但因本文讨论App的存储位置,因此本文不做特殊说明的话,均是指以相对于App而言的“内”和“外”。
除此之外,Environment类 还有其他的目录结构获取方法,如下:
3 读写内外存储空间的文件
3.1 读写App内部存储空间的文件
3.1.1 读文件 - Internal
代码
/** * 读Internal中文件的方法 * * @param filePathName 文件路径及文件名 * @return 读出的字符串 * @throws IOException */ public static String readInternal(String filePathName) throws IOException { StringBuffer stringBuffer = new StringBuffer(); // 打开文件输入流 FileInputStream fileInputStream = new FileInputStream(filePathName); byte[] buffer = new byte[1024]; int len = fileInputStream.read(buffer); // 读取文件内容 while (len > 0) { stringBuffer.append(new String(buffer, 0, len)); // 继续将数据放到buffer中 len = fileInputStream.read(buffer); } // 关闭输入流 fileInputStream.close(); return stringBuffer.toString(); }
3.1.2 写文件 - Internal
代码
/** * 写Internal中文件的方法 * * @param filePathName 文件路径及文件名 * @param content 要写进文件中的内容 * @throws IOException */ public static void writeInternal(String filePathName, String content) throws IOException { // 打开文件输出流 FileOutputStream fileOutputStream = new FileOutputStream(filePathName); // 写数据到文件中 fileOutputStream.write(content.getBytes()); // 关闭输出流 fileOutputStream.close(); }
3.2 读写App外部存储空间的文件
3.2.1 读文件 - External
代码
/** * 从External文件目录下读取文件 * * @param filePathName 要读取的文件的路径+文件名 * @return * @throws IOException */ public static String readExternal(String filePathName) throws IOException { StringBuffer stringBuffer = new StringBuffer(); // 获取External的可用状态 String storageState = Environment.getExternalStorageState(); if (storageState.equals(Environment.MEDIA_MOUNTED)) { // 当External的可用状态为可用时 // 打开文件输入流 FileInputStream fileInputStream = new FileInputStream(filePathName); byte[] buffer = new byte[1024]; int len = fileInputStream.read(buffer); // 读取文件内容 while (len > 0) { stringBuffer.append(new String(buffer, 0, len)); // 继续把数据存放在buffer中 len = fileInputStream.read(buffer); } // 关闭输入流 fileInputStream.close(); } return stringBuffer.toString(); }
3.2.2 写文件 - External
代码
/** * 向External文件目录下写入文件 * * @param filePathName 要写入的的文件的路径+文件名 * @param content 要写入的内容 * @throws IOException */ public static void writeExternal(String filePathName, String content) throws IOException { // 获取External的可用状态 String storageState = Environment.getExternalStorageState(); if (storageState.equals(Environment.MEDIA_MOUNTED)) { // 当External的可用状态为可用时 // 打开输出流 FileOutputStream fileOutputStream = new FileOutputStream(filePathName); // 写入文件内容 fileOutputStream.write(content.getBytes()); // 关闭输出流 fileOutputStream.close(); } }
4 验证读写功能及权限
首先选取一个高版本的测试机,众所周知,Android6.0增加了动态权限的申请步骤。本人用的MI2S为Android 5.0就先暂时放一边,换上一个Android8.0的Galaxy S7。本部分改变一下顺序,先写再读。
4.1 读写Internal文件
4.1.1 写入Internal文件
首先找一下 data/data/路径下没有“com.dandelion.abc”的包名
安装该测试应用后出现该包名,但cache目录下为空(下图左)。然后去应用设置中关闭本软件的读写权限,
之后执行写入命令,向cache/files目录下分别写入文件,然后查看文件路径(上图右)
String content = "Always believe that something wonderful is about to happen!";// 获取文件在Internal中文件目录下的路径String pathInternalCache = this.getCacheDir().getAbsolutePath() + File.separator + "InternalCacheTest.txt";String pathInternalFiles = this.getFilesDir().getAbsolutePath() + File.separator + "InternalFilesTest.txt";// 写入操作try { writeInternal(pathInternalCache, content); writeInternal(pathInternalFiles, content);} catch (IOException e) { e.printStackTrace();}
说明写入成功
4.1.2 读出Internal文件
现在把上一步写入的文件读出
// 获取文件在Internal中文件目录下的路径String pathInternalCache = this.getCacheDir().getAbsolutePath() + File.separator + "InternalCacheTest.txt";String pathInternalFiles = this.getFilesDir().getAbsolutePath() + File.separator + "InternalFilesTest.txt";// 读出操作String textReadInternalCache = null;String textReadInternalFiles = null;try { textReadInternalCache = readInternal(pathInternalCache); textReadInternalFiles = readInternal(pathInternalFiles);} catch (IOException e) { e.printStackTrace();}// 日志打印log.d(TAG, pathInternalCache); // 路径,下同log.d(TAG, textReadInternalCache); // 内容,下同log.d(TAG, pathInternalFiles);log.d(TAG, textReadInternalFiles);
输出结果:
说明读出成功,而此时,该应用并未开启外部存储读写权限,说明读写该目录下的文件不需要外部存储读写权限。
卸载该应用,该目录下的本应用包名及其文件均被删除。
4.2 读写External包名区文件
首先应该明确,该区域的存储位置不仅可以从Device File Explorer查看,还可以通过手机的文件管理查看
首先查找一下mnt/sdcard/Android/data/目录下的“com.dandeion.abc”的包名,发现无论从Android Studio的Device File Explorer还是手机的文件管理,均找不到“com.dandeion.abc”的包名,说明当前未向该区域存储文件
然后去应用设置中关闭本软件的外部存储读写权限。
4.2.1 写入External包名区文件
String content = "Always believe that something wonderful is about to happen!";// 获取文件在External-Android//cache文件目录下的路径String pathExternalCache = this.getExternalCacheDir().getAbsolutePath() + File.separator + "ExternalCacheTest.txt";String pathExternalFiles = this.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + File.separator + "ExternalFilesTest.txt";// 写入操作try { writeExternal(pathExternalCache, content); writeExternal(pathExternalFiles, content);} catch (IOException e) { e.printStackTrace();}
查看目录(下图左:Device File Explorer;下图右:手机文件管理)
发现写入成功了,而此时该应用并未获取到外部读写权限。
为了说明这一点,继续做一点更绝的,打开AndroidManifest.xml文件,把该文件中的读写权限的声明一并删除,并在手机上卸载该应用后重新安装。
结果:
Internal应用区:data/data/<包名>/[cache|files] 目录写入成功!
External包名区:mnt/sdcard/Android/data/<包名>/[cache|files] 目录写入成功!
因此可以得出结论:
Internal应用区和External包名区的读写均不需要应用开发者声明和获取外部存储读写权限。该权限是指:
permission.READ_EXTERNAL_STORAGE
permission.WRITE_EXTERNAL_STORAGE
4.2.2 读出External包名区文件
读出
// 获取文件在External-Android//[cache|files]文件目录下的路径String pathExternalCache = this.getExternalCacheDir().getAbsolutePath() + File.separator + "ExternalCacheTest.txt";String pathExternalFiles = this.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + File.separator + "ExternalFilesTest.txt";// 读出操作String textReadExternalCache = null;String textReadExternalFiles = null;try { textReadExternalCache = readExternal(pathExternalCache); textReadExternalFiles = readExternal(pathExternalFiles);} catch (IOException e) { e.printStackTrace();}// 日志输出log.d(TAG, pathExternalCache);log.d(TAG, textReadExternalCache);log.d(TAG, pathExternalFiles);log.d(TAG, textReadExternalFiles);
输出
说明读出成功,而此时,该应用并未开启外部存储读写权限,说明读写该目录下的文件不需要外部存储读写权限。
卸载该应用,该目录下的本应用包名及其文件均被删除。
4.3 读写External Storage自由区的文件
类似4.2,该区域的存储位置,可从Device File Explorer查看,还可以通过手机的文件管理查看
因实验打算向mnt/sdcard/Android/[ Ducument | Dandelion ] 目录下写入文件,因此先查看一下未写入时的目录情况,以留作比较
然后去应用设置中关闭本软件的外部存储读写权限。
4.3.1 向External Storage自由区写入文件
写入
String content = "Always believe that something wonderful is about to happen!";// 获取文件在External自由区(/storage/emulated/0/具体目录)文件目录下的路径String pathStorageRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "StorageRootTest.txt";String pathStoragePublic = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + File.separator + "StoragePublicTest.txt";// 需要创建文件夹目录的路径String dirStorageDandelion = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Dandelion";String pathStorageDandelion = dirStorageDandelion + "/StorageDandelionTest.txt";// 创建文件夹【注意:因本路径本身没有,需要单独创建,不创建就引用会报FileNotFoundException】boolean dirExist = makeDirectory(dirStorageDandelion);// 写入操作try { writeExternal(pathStorageRoot, content); writeExternal(pathStorageDandelion, content); if (dirExist) writeExternal(pathStorageDandelion, content); // 需要创建目录的路径} catch (IOException e) { e.printStackTrace();}
执行完毕之后查看目录结构,发现与刚才的目录并无差异。是何原因?
未崩溃是因为所执行的步骤都在try--catch内部,自然不会崩溃报Error,所以找一下Warning吧~查找日志发现:
W/System.err: java.io.FileNotFoundException: /storage/emulated/0/StorageRootTest.txt (Permission denied)
Permission denied
外部存储读写权限的问题。所以,自此开始,就需要:
① 在AndroidManifest.xml文件中增添如下权限声明:
② 并在Java代码中,动态申请该敏感权限。动态申请部分此处略去一万字。
由此可知,读写该区域目录下的文件,是需要读写权限的。
添加“声明并动态申请权限”的代码后,继续操作,得出如下文件:
4.3.2 从External Storage自由区读出文件
读出操作
// 获取文件在External自由区(/storage/emulated/0/具体目录)文件目录下的路径String pathStorageRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "StorageRootTest.txt";String pathStoragePublic = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + File.separator + "StoragePublicTest.txt";String pathStorageDandelion = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Dandelion/StorageDandelionTest.txt";// 读出操作String textReadStorageRoot = null;String textReadStoragePublic = null;String textStorageDandelion = null;try { textReadStorageRoot = readExternal(pathStorageRoot); textReadStoragePublic = readExternal(pathStoragePublic); textStorageDandelion = readExternal(pathStorageDandelion);} catch (IOException e) { e.printStackTrace();}// 日志输出log.d(TAG, pathStorageRoot);log.d(TAG, textReadStorageRoot);log.d(TAG, pathStoragePublic);log.d(TAG, textReadStoragePublic);log.d(TAG, pathStorageDandelion);log.d(TAG, textStorageDandelion);
输出
说明读出成功,而此时,该应用必须开启外部存储读写权限,说明读写该目录下的文件必须获取外部存储读写权限。
卸载该应用,该目录下的本应用包名及其文件均未被自动删除,说明该目录下可长期存放某些文件。
更多相关文章
- 14 Android(安卓)android 按钮效果的两种实现方法
- android的文件操作
- Android如何在xml布局中使用自定义属性
- cocos2d-x android
- android用sharepreference保存输入框中的内容
- Android(安卓)7.0 中 FileProvider空指针
- android 文件管理工具类
- Android(安卓)应用文件路径
- android 文件管理工具类