前面几篇随笔 讲到的关于存储的,SharedPreferences、Room、数据库等 最终都是以文件形式 存储到手机上的(除特殊的存储于手机内存的:如Room可以创建内存数据库)。

这些存储方式,Android都提供了相应的API 方便操作数据:

SharedPreferences:最终存储为一个xml文件。

数据库:以数据库的形式存储,在手机中是一个.db的文件。前面有讲过两种 一种是Room,一种是ContentProvider。

下面,详细的介绍下在Android 直接读写文件

一、内部存储和外部存储

这里的存储指的是永久非易失的存储(rom),不是内存(ram)。

这里的外部存储不是指 特定的可移动的存储介质(如SD卡),现在很多Android手机都是一体机,普通消费者无法拆卸的,也没有扩展SD卡的的卡槽了,我们把手机本身自带的rom叫机身存储或内置存储现在的Android设备都将机身存储 划分了内部存储和外部存储,体现内部和外部的区别。如果可扩展可移动存储(如SD卡),这也是外部存储,这样手机则包含多个外部存储。

注:下面的讨论都是基于Android 4.4之后的,因为在老的Android版本(4.4之前)上 机身存储没有划分内部存储和外部存储,机身存储即内部存储,SD卡即外部存储。

因为外部存储事可移动的 可移除的,存在明显的差异,它们具体不同的使用场景和功能,下面是大致差异,后面再具体说明。

内部存储

  • 可用: 一直可用
  • 访问:应用保存文件在这里,只有应用本身能访问
  • 卸载:卸载应用时,应用的所有文件都会从内部存储中删除

外部存储

  • 可用:非一直可用,可用挂载外部存储作为USB存储,甚至可用移除外部存储
  • 访问:存储在外部存储的文件,很容易被其他应用访问和修改
  • 卸载:卸载应用时,应用在外部存储创建的文件不一定都会被删除掉,只是在getExternalFilesDir()路径下的文件会被删除(后面会具体说明)

1、内部存储

当存储在内部存储时,可用通过下面方法获取合适的File对象。

Context   getFilesDir(): 返回应用程序的内部files目录的File对象。如:/data/user/0/com.flx.testfilestorage/files

Context   getCacheDir():返回一个应用程序内部临时文件目录的File对象,这个临时文件在不需要时会被删除,在系统可用存储很低时也会被系统删除。如:/data/user/0/com.flx.testfilestorage/cache

应用files目录中文件的操作

创建:这里给出了如下两种方式

  • 构造File对象,包含了目录和文件名。通过createNewFile()进行创建,如果文件不存在则创建。
File createFiles = new File(context.getFilesDir(), "testfile.txt");
try {
createFiles.createNewFile();
} catch (IOException e) {
Log.d( TAG, "files err:"+e.getMessage() );
}
  • 通过Context的api直接操作,通过openFileOutput直接获取FileOutputStream输出流对象,如果文件不存在,则直接创建。

注:openFileOutput的第二个参数(mode)文件的操作模式,其他值在API 17以后被弃用,只能使用MODE_PRIVATE。在SharedPreferences中有详细说明:https://www.cnblogs.com/fanglongxiang/p/11390013.html

        try {
//mode参数注意下,这里使用的Context.MODE_PRIVATE
FileOutputStream outputStream = context.openFileOutput( "testfile22.txt", Context.MODE_PRIVATE );
outputStream.write( "Use OutputStream Create file\n".getBytes() );
outputStream.close();
} catch (IOException e) {
Log.d( TAG, "outputStream err:"+e.getMessage() );
}

获取文件:

  • 同上,通过目录和文件名 构造File对象
File createFiles = new File(context.getFilesDir(), "testfile.txt");
  • 上面通过openFileOutput获取文件输出流 可写文件(不存在则创建),也可通过openFileInput获取文件输入流 进行读取文件内容。
FileOutputStream outputStream = context.openFileOutput( "testfile22.txt", Context.MODE_PRIVATE );//输出流
FileInputStream fileInputStream = context.openFileInput( "testfile22.txt" );//输入流
  • 还可直接获取files里的所有文件列表
String[] arrFiles = context.fileList();

  

读写文件:

直接构造文件输出输入流读写文件。

FileOutputStream fileOutputStream = context.openFileOutput( "testfile22.txt", Context.MODE_PRIVATE );
FileOutputStream fileOutputStream2 = new FileOutputStream( createFiles ); FileInputStream fileInputStream = context.openFileInput( "testfile22.txt" );
FileInputStream fileInputStream2 = new FileInputStream( createFiles );

  

删除文件:

  • File对象直接调用delete()方法删除
createFiles.delete();
  • Context直接通过文件名删除
context.deleteFile( "testfile22.txt" );

  

创建临时文件:

上述files目录操作方式同样适用(File对象需要指定路径的可以,而Context直接通过文件名的则不可以),另外添加一个createTempFile()的方法。

File.createTempFile( "tempfile", null, context.getCacheDir() );

  

2、外部存储

首先,简单介绍下两个方法的差异以及主外部存储。

先看下这段代码,

String state = Environment.getExternalStorageState();
File externalFile = context.getExternalFilesDir( null );
File[] externalFiles = context.getExternalFilesDirs( Environment.DIRECTORY_PICTURES );
for (File file : externalFiles) {
Log.d( TAG, "state="+ state + ";\nexternalFiles=" + file + ";\nexternalFile="+externalFile);
try {
FileOutputStream fileOutputStream = new FileOutputStream( new File( file, "aaaa.txt" ) );
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

运行的手机支持SD卡 并插入了一张SD卡,看看运行结果

2019-07-15 14:46:07.819 12704-12704/com.flx.testfilestorage D/flx_storage: state=mounted;
externalFiles=/storage/emulated/0/Android/data/com.flx.testfilestorage/files/Pictures;
externalFile=/storage/emulated/0/Android/data/com.flx.testfilestorage/files
2019-07-15 14:46:07.821 12704-12704/com.flx.testfilestorage D/flx_storage: state=mounted;
externalFiles=/storage/553C-0E05/Android/data/com.flx.testfilestorage/files/Pictures;
externalFile=/storage/emulated/0/Android/data/com.flx.testfilestorage/files

从上面看到,getExternalFilesDirs获取的有两个外部存储,getExternalFilesDir是一个。这两个外部存储,一个是主外部存储 即手机本身存储中分为 内部存储和外部存储的 外部存储部分,另一个是SD卡的挂载路径。

getExternalFilesDir(),获取就是主外部存储路径。

getExternalFilesDirs(),获取所有外部存储的路径,包括本身的外部存储 和 扩展出来的存储(如SD卡)。

在一开始就说过,应用存储到外部存储的文件 当应用卸载时只有getExternalFilesDir()路径下的会被删除。

上面代码在log后,所有外部存储路径下 都创建了aaaa.txt的文件,实际操作结果也是符合的,当卸载应用时,/storage/553C-0E05/Android/data/com.flx.testfilestorage/files/这个下面的aaaa.txt 仍然存在的。

可用性判断: 

由于外部存储不一定一直有用,可能被移除等情况,所以使用外部存储需要进行判断,外部存储是否可用。

Environment.getExternalStorageState([File path])这个方法可以返回外部存储的状态,不过它只返回主外部存储的状态。从源码中,也能直接的看出来

 保存文件到公共目录:

保存文件到外部存储的公共目录,其他应用也能直接访问,有以下两种方法(非直接创建读写文件)

  • 媒体文件如图片、音频文件、录像文件等,使用MediaStore的API进行操作。
  • 保存其他文档文件,如PDF,使用ACTION_CREATE_DOCUMENT intent操作。这个就属于SAF(Storage Access Framework)相关的了。

注:如果媒体文件,不希望被 Media Scanner 扫描,可以在相应目录创建一个空的文件命名为 .nomedia  。这样就能阻止Media Scanner的扫描和读取文件 并 通过MediaStore的API提供给其他应用使用。

保存文件到私有目录:

应用保存文件到外部存储的私有目录,可以使用getExternalFilesDir()方法。具体实例 参考外部存储开始的示例。

注意:1.注意getExternalFilesDir()和getExternalFilesDirs()的区别、使用。

      2.getExternalFilesDir()里的文件 会随着应用的卸载而删除。

   3.注意这两个方法的参数,尽量正确使用API提供的常量,这样系统才能正确处理文件。如:使用Environment.DIRECTORY_RINGTONES,系统media scanner能够识别到是铃声而不是音乐。

二、存储路径

从上面的介绍可用看到,应用存储在 内部存储和外部存储上的私有目录(getExternalFilesDir())下的,是和应用相关的,在应用卸载时 对应目录文件会被删除。

下面列举了常用的存储路径,

        int i = 0;
for (File file:context.getExternalFilesDirs( null )) {
Log.d( TAG, " \ncontext.getExternalFilesDirs()[" + ++i + "]=" + file );
}
i=0;
Log.d( TAG, " \ncontext.getExternalFilesDir()=" + context.getExternalFilesDir( null ) );
Log.d( TAG, " \ncontext.getCacheDir()=" + context.getCacheDir() );
Log.d( TAG, " \ncontext.getCodeCacheDir()=" + context.getCodeCacheDir());
Log.d( TAG, " \ncontext.getDatabasePath()=" + context.getDatabasePath( "external.db" ));
Log.d( TAG, " \ncontext.getDataDir()=" + context.getDataDir() );
Log.d( TAG, " \ncontext.getDir()=" + context.getDir( null, Context.MODE_PRIVATE ) );
Log.d( TAG, " \ncontext.getExternalCacheDir()=" + context.getExternalCacheDir() );
Log.d( TAG, " \ncontext.getFilesDir()=" + context.getFilesDir() );
Log.d( TAG, " \ncontext.getObbDir()=" + context.getObbDir() );
for (File file:context.getExternalCacheDirs()) {
Log.d( TAG, " \ncontext.getExternalCacheDirs()[" + ++i + "]=" + file );
} Log.d( TAG, " \nEnvironment.getDataDirectory()=" + Environment.getDataDirectory() );
Log.d( TAG, " \nEnvironment.getExternalStorageDirectory()="+Environment.getExternalStorageDirectory() );
Log.d( TAG, " \nEnvironment.getDownloadCacheDirectory()="+Environment.getDownloadCacheDirectory() );
Log.d( TAG, " \nEnvironment.getRootDirectory()="+Environment.getRootDirectory() );
Log.d( TAG, " \nEnvironment.getExternalStoragePublicDirectory()= "+Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES ) );

log打印出具体路径:

2019-09-11 16:38:08.799 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getExternalFilesDirs()[1]=/storage/emulated/0/Android/data/com.flx.testfilestorage/files
2019-09-11 16:38:08.799 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getExternalFilesDirs()[2]=/storage/553C-0E05/Android/data/com.flx.testfilestorage/files
2019-09-11 16:38:08.803 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getExternalFilesDir()=/storage/emulated/0/Android/data/com.flx.testfilestorage/files
2019-09-11 16:38:08.805 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getCacheDir()=/data/user/0/com.flx.testfilestorage/cache
2019-09-11 16:38:08.806 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getCodeCacheDir()=/data/user/0/com.flx.testfilestorage/code_cache
2019-09-11 16:38:08.807 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getDatabasePath()=/data/user/0/com.flx.testfilestorage/databases/external.db
2019-09-11 16:38:08.807 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getDataDir()=/data/user/0/com.flx.testfilestorage
2019-09-11 16:38:08.808 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getDir()=/data/user/0/com.flx.testfilestorage/app_null
2019-09-11 16:38:08.813 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getExternalCacheDir()=/storage/emulated/0/Android/data/com.flx.testfilestorage/cache
2019-09-11 16:38:08.814 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getFilesDir()=/data/user/0/com.flx.testfilestorage/files
2019-09-11 16:38:08.818 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getObbDir()=/storage/emulated/0/Android/obb/com.flx.testfilestorage
2019-09-11 16:38:08.823 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getExternalCacheDirs()[1]=/storage/emulated/0/Android/data/com.flx.testfilestorage/cache
2019-09-11 16:38:08.824 19469-19469/com.flx.testfilestorage D/flx_storage:
context.getExternalCacheDirs()[2]=/storage/553C-0E05/Android/data/com.flx.testfilestorage/cache
2019-09-11 16:38:08.824 19469-19469/com.flx.testfilestorage D/flx_storage:
Environment.getDataDirectory()=/data
2019-09-11 16:38:08.828 19469-19469/com.flx.testfilestorage D/flx_storage:
Environment.getExternalStorageDirectory()=/storage/emulated/0
2019-09-11 16:38:08.828 19469-19469/com.flx.testfilestorage D/flx_storage:
Environment.getDownloadCacheDirectory()=/data/cache
2019-09-11 16:38:08.828 19469-19469/com.flx.testfilestorage D/flx_storage:
Environment.getRootDirectory()=/system
2019-09-11 16:38:08.832 19469-19469/com.flx.testfilestorage D/flx_storage:
Environment.getExternalStoragePublicDirectory()= /storage/emulated/0/Pictures

Context获取的都是包含包名的路径。仔细看下就能知道 方法获取的是手机中的路径。

这些方法获取到的都是File对象,可用使用getTotalSpace()或getFreeSpace()获取总空间大小或剩余空间大小,注意单位时byte.

下面是 /data/user/0/com.flx.testfilestorage/ 在手机中的显示,不是所有应用下面的文件夹都有的,一些是上面运行产生的。正常如果没有对应文件 则对应文件夹是不存在的,如没有数据库文件 则databases是没有的,如果有SharedPreferences数据则还会有shared_prefs文件夹。不过cache,code_cache,files一般都是有的。

 路径疑问:

以前随笔中介绍的,很多路径是/data/data/xxx,如SharedPreferences存储在/data/data/<packagename>/shared_prefs/下,数据库存储在/data/data/<package_name>/databases下。而上面获取到的是/data/user/0/xxxx下,这是怎么回事呢?

通过Android Studio看下/data/data/com.flx.testfilestorage/ 里面的内容可以看到是一样的.

其实,/data/user/0/ 就是链接的/data/data/.所以他们的目标目录都是/data/data/xxx下的。

还可以通过命令查看,第一位的l就是表示一个链接。

Android系统中,这样的链接还有不少。如果对路径有疑问,可以通过这两个方法看下,是否是链接的文件

Android_存储之文件存储的更多相关文章

  1. Android 数据存储之 文件存储

    -------------------------------------------文件存储----------------------------------------------- 文件存储是 ...

  2. Android开发手记(17) 数据存储二 文件存储数据

    Android为数据存储提供了五种方式: 1.SharedPreferences 2.文件存储 3.SQLite数据库 4.ContentProvider 5.网络存储 本文主要介绍如何使用文件来存储 ...

  3. android 开发-数据存储之文件存储

    android的文件存储是通过android的文件系统对数据进行临时的保存操作,并不是持久化数据,例如网络上下载某些图片.音频.视频文件等.如缓存文件将会在清理应用缓存的时候被清除,或者是应用卸载的时 ...

  4. Android学习——数据存储之文件存储

    将数据存储到文件中并读取数据 1.新建FilePersistenceTest项目,并修改activity_main.xml中的代码,如下:(只加入了EditText,用于输入文本内容,不管输入什么按下 ...

  5. 【Linux】【Basis】块存储,文件存储,对象存储

    1. 块存储: 定义:这种接口通常以QEMU Driver或者Kernel Module的方式存在,这种接口需要实现Linux的Block Device的接口或者QEMU提供的Block Driver ...

  6. Android数据存储之文件存储

    首先给大家介绍使用文件如何对数据进行存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的. public ...

  7. 获取tomcat上properties文件的内容——方便文件存储位置的修改,解耦和

    在java web开发的时候经常会用到读取读取或存放文件,这个文件的默认路径在哪里呢?写死在程序里面显然是可以的,但这样子不利于位于,假如有一天项目从window移植到linux,或者保存文件的路径变 ...

  8. 【Android】数据存储-java IO流文件存储

    1.数据持久化:将在内存中的瞬时数据保存在存储设备中.瞬时数据:设备关机数据丢失.持久化技术提供一种机制可以让数据在瞬时状态和持久状态之间转换. 2.Android中简单的三种存储方式:文件存储.Sh ...

  9. android 开发-文件存储之读写sdcard

    android提供对可移除的外部存储进行文件存储.在对外部sdcard进行调用的时候首先要调用Environment.getExternalStorageState()检查sdcard的可用状态.通过 ...

随机推荐

  1. Pika源码学习--pika的通信和线程模型

    pika的线程模型有官方的wiki介绍https://github.com/Qihoo360/pika/wiki/pika-%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B,这 ...

  2. 《C程序设计语言》 练习1-23

    问题描述 编写一个删除C语言程序中所有的注释语句.要正确处理带引号的字符串与字符常量.在C语言中,注释不允许嵌套. Write a program to remove all comments fro ...

  3. 在Vue中使用iview的Select控件实现一个多级选项列表

    前言 今天项目要实现一个多级选项列表,发现iview官网上没有写这个例子,于是自己就实现了,如果对你有帮助请点个赞 ‘ * ’!! 解决方法:下面我们就来使用V-for 来定义一个二级选项列表 ,代码 ...

  4. 最长递增子序列(Longest increasing subsequence)

    问题定义: 给定一个长度为N的数组A,找出一个最长的单调递增子序列(不要求连续). 这道题共3种解法. 1. 动态规划 动态规划的核心是状态的定义和状态转移方程.定义lis(i),表示前i个数中以A[ ...

  5. 常用设计模式的实现,以及Netty中的设计模式

    1.观察者模式 有两个角色,观察者和被观察者.当被观察者发出消息后,注册了的观察者会收到其消息,而没有注册的观察者就不会收到. //定义观察者接口 interface Observer{ //通知观察 ...

  6. 【HBase】快速了解上手rowKey的设计技巧

    目录 为什么要设计rowKey 三大原则 长度原则 散列原则 唯一原则 热点问题的解决 加盐 哈希 反转 时间戳反转 为什么要设计rowKey 首先要弄明白一点,Regions的分区就是根据数据的ro ...

  7. python工业互联网应用实战1—SQL与ORM

    从sql到ORM应该说也是编程体系逐步演化的结果,通过类和对象更好的组织开个过程中遇到的各种业务问题,面向对象的解耦和内聚作为一套有效的方法论,对于复杂的企业应用而言确实能够解决实践过程中很多问题. ...

  8. Java Stream 流如何进行合并操作

    1. 前言 Java Stream Api 提供了很多有用的 Api 让我们很方便将集合或者多个同类型的元素转换为流进行操作.今天我们来看看如何合并 Stream 流. 2. Stream 流的合并 ...

  9. Autohotkey心得

    玩游戏,烧钱和作弊是永恒的话题,热键一定程度上和作弊相关.办公用数据库.编程.商业智能,一定程度上也是作弊,欺负没有相关信息技术的公司.个人. 避免和输入法产生冲突,少用Send,多用剪切板中转. E ...

  10. [hdu4713 Permutation]DP

    题意:将一个数拆成若干数的和使得它们的最小公倍数最大 思路:一个数x可以拆成p1k1 + p2k2 + ... + pnkn形式,其中pi是质数或1.对于最小公倍数最大的情况,一定可以表示成这种形式. ...