补充: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);

输出

说明读出成功,而此时,该应用必须开启外部存储读写权限,说明读写该目录下的文件必须获取外部存储读写权限。

卸载该应用,该目录下的本应用包名及其文件均未被自动删除,说明该目录下可长期存放某些文件。

更多相关文章

  1. 14 Android(安卓)android 按钮效果的两种实现方法
  2. android的文件操作
  3. Android如何在xml布局中使用自定义属性
  4. cocos2d-x android
  5. android用sharepreference保存输入框中的内容
  6. Android(安卓)7.0 中 FileProvider空指针
  7. android 文件管理工具类
  8. Android(安卓)应用文件路径
  9. android 文件管理工具类

随机推荐

  1. Android(安卓)ImageView图片自适应
  2. android studio运行时报错AVD Nexus_5X_A
  3. 【笔记】Android高版本Apache HTTPClient
  4. Delphi处理Android的路径信息
  5. 查看移动App排名和推荐的网站分享
  6. Android通过onDraw实现在View中绘图操作
  7. android 布局实例解析 柱状图效果
  8. Android(安卓)扫码器串口通讯
  9. Android(安卓)CTS 测试总结
  10. OpenGL ES 学习笔记(一)