Android 数据存储——Saving Files

来源:互联网 发布:sar指标公式源码java 编辑:程序博客网 时间:2024/05/20 08:23
文章绝大部分来自Android官网文件存储

1.概述

  Android的APP中经常需要对大文件进行操作,而操作这些大文件需要了解Android的文件系统(或者说是Linux文件系统)有关的知识。当需要读写大量的数据的时候,我们需要用到File对象,来对文件进行操作。

   上面是官网的大意,简单一点来说就是,当我们的APP需要进行大文件操作时,会对设备进行文件读写操作,而Android手机上,存储文件用到最多的就是设备的SD卡了。(当然这里讲的并不局限于SD卡)

2.内部存储外部存储

  一般来说,Android会把文件存储区域分为内部存储外部存储,这种说法来源于Android早期的时候,它把文件存储区域分成了两块,内置存储空间(设备自带的存储区域),可扩展存储空间(比如说SD卡这一类东西),当然也有一些设备把自身的存储区域或分成两个分区,一个是内部存储(internal),一个是外部存储(external),因此我们会发现即便是有的设备上没插SD卡,也会有两个存储空间。更直观感受,我们在安装应用的时候,可能会选择安装到手机上,也可以选择安装到SD上,就是这样了。

  Android官网上列出了这两种存储的不同。

 内部存储(internal storage)

  • 总是可以获取到的(available)
  • 默认情况下,只有你自己的APP可以访问到这片空间
  • 卸载APP之后,系统会移除你的APP的所有相关文件

 外部存储(external storage)

  • 并不总是可以获取的,用户可能会挂载一些USB设备(比如SD卡什么的),当移除了这些设备之后,就获取不到这片空间了
  • 这片空间是谁都可以访问的
  • 只要你存文件的目录是通过调用getExternalFilesDir()获得的,卸载APP时,程序的文件就不会被删除

  tip: 一般安装APP时,系统都会默认安装在内部存储空间中,但是我们可以在AndroidManifest.xml中通过设置 android:installLocation来改变安装路径。一般来说,当APP的安装包特别的大时,尽量还是把程序安装到外部存储空间,毕竟,外部存储空间比内部存储空间还是大很多的。

  上面说了这么多,可能还是不好理解,如果你用过以前低版本的Android手机,安装APP的时候,可能会让你选择,安装到手机还是安装到内存卡中,这其实就是内部存储和外部存储最直观的感受了。如果一定要直观的感受,你可以认为内部存储是手机自带的空间(不是现在很多手机的内置SD卡),外部存储是自己装上的SD卡。

3.获取权限

  要向外部存储空间写入数据的话,必须加入权限,否则的话,程序会报错。需要在AndroidManifest.xml中这么声明:

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

  注意:目前来说,所以的APP都有读取外部存储空间的权限,但是,官网上说了,现在有权限,以后就不一定了,指不定哪一天Google就限制了这条权限,所以为了保险起见,要是对外部存储空间执行读操作的时候,最好还是把“读”的权限加上,免得以后出问题。

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

还有一点也要注意,只要声明了“写”的权限,那么就一定有“读”的权限,这点不用担心。

  上面说的是外部存储空间,内部存储空间没有这么麻烦,不需要声明权限,因为所有的APP内内部存储空间都有读写权限,无需额外的权限声明。

4.在内部存储空间中存储文件

  在内部存储空间中存储文件时,显然第一步是要获取路径,这个路径是一个文件对象(File),有两种方法
  • getFileDirs()
  •   这种方法返回一个File对象,就相当于你的APP的内部存储空间的路径。这个方法返回的路径是/data/data/package_name/files

  • getCacheDir()
  •   显然从方法名上就可以看出来,这是获取APP的缓存路径,返回值同样是一个File对象,不过是缓存的路径。使用这个方法保存文件时需要注意,因为这里是缓存目录,一旦文件使用完毕,请及时删除,也不要存太多(或太大)的文件在这儿,系统发现存储空间不足的时候,会优先删除这里的文件,还不给任何的⚠️警告提示。
      这个方法得到的路径是/data/data/package_name/cache

  通过上面的的两种方法获取到路径之后,就可以对文件进行操作了,操作方法就是Java中对文件的操作。可以用下面的方法获取到文件对象:

File file = new File(context.getFilesDir(), filename);

  除了上面获取文件对象的方法,还可以用openFileOutput()方法获取一个FileOutputStream的对象,通过输出流对文件进行操作:

String filename = "myfile";String string = "Hello world!";FileOutputStream outputStream;try {  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);  outputStream.write(string.getBytes());  outputStream.close();} catch (Exception e) {  e.printStackTrace();}

  要是想对文件进行缓存操作的话,需要使用createtTempFile(),比如下面的例子,通过url拿到文件,并存在缓存目录下:

public File getTempFile(Context context, String url) {    File file;    try {        String fileName = Uri.parse(url).getLastPathSegment();        file = File.createTempFile(fileName, null, context.getCacheDir());    catch (IOException e) {        // Error while creating file    }    return file;}
  Note:每一个APP的内部存储目录都是通过包名唯一确定的,严格来说,其他APP要想访问到你的APP的内部存储的文件,必须要知道你的APP的包名,以及要访问的文件名,但是,光靠这些还是不够的,只有你对文件模式设置为“可读”他们才有机会读取到相应的文件。仅当你对APP的内部存储的文件设置了“可读”和“可写”的权限时,其他的APP才有读写内部的权限。如果,你把这里的文件的属性设置成私有的(MODE_PRIVATE),那其他的APP将永远没有机会访问到相应的文件。

5. 在外部存储空间中存储文件

  上面提到了,外部存储并不总是可以访问到的,因为它又可能没挂载(mount)到Android的文件系统上。比方说,你把手机连到电脑上,然后又打开了“作为媒体设备”(当然不同的手机提示不一样),或者是你把SD卡拔出来了,显然就访问不到了。专业一点来说,外部存储设备被从Android的文件系统上“卸载(unmounted)”了。

  所以说,要在外部存储空间上存储文件的时候,一定要先判断一下它的状态,要不然可能会出错。通过getExternalStorageState()方法获取到外部存储的状态,如果状态是MEDIA_MOUNTED,那就可以读写了。下面的两个方法就是用来判断外部存储的状态的。

/* Checks if external storage is available for read and write */public boolean isExternalStorageWritable() {    String state = Environment.getExternalStorageState();    if (Environment.MEDIA_MOUNTED.equals(state)) {        return true;    }    return false;}/* Checks if external storage is available to at least read */public boolean isExternalStorageReadable() {    String state = Environment.getExternalStorageState();    if (Environment.MEDIA_MOUNTED.equals(state) ||        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {        return true;    }    return false;}
  外部存储的空间是其他人也可以访问到的,一般来说,存在外部存储的文件应当分为两种不同的目录,一个是公有的,一个是私有的。

  • 公有文件
  •   这样的目录下的文件应该是其他APP和用户所共享的,当你自己的APP被卸载时,这里的文件应当被保留。这类文件,举个例子,系统的图片文件夹,音乐文件夹,下载目录等。

  • 私有文件
  •   这类文件是你的APP的私有文件,当你的APP被卸载时,这里的文件会被删除。尽管说是私有文件,实际上,其他人或程序还是可以访问到的。这类文件,举例说,在APP的中下载的临时文件。

  那么怎么获取存储这两种文件的文件夹呢?方法很简单,分别调用getExternalStoragePublicDirectory()和getExternalFilesDir()这两个方法就可以,下面是这两个方法的具体用法,区别还是很明显的:

获取公有相册文件夹

public File getAlbumStorageDir(String albumName) {    // Get the directory for the user's public pictures directory.     File file = new File(Environment.getExternalStoragePublicDirectory(            Environment.DIRECTORY_PICTURES), albumName);    if (!file.mkdirs()) {        Log.e(LOG_TAG, "Directory not created");    }    return file;}

获取自己的相册文件夹:

public File getAlbumStorageDir(Context context, String albumName) {    // Get the directory for the app's private pictures directory.     File file = new File(context.getExternalFilesDir(            Environment.DIRECTORY_PICTURES), albumName);    if (!file.mkdirs()) {        Log.e(LOG_TAG, "Directory not created");    }    return file;}

   注意getExternalFilesDir()的参数,如果穿一个null的话,会返回外部存储的根目录。
  在使用的时候,一定要注意文件类型,然后选择相应的存储方式,不要用混了。比如说,下载了一些文件,本来是想一直保存着的,但是,却调用了上面的第二种方法,然后,当APP被卸载之后,下载的文件就被系统删除了。
  还有,官网上建议,在使用上面第一种方法时,传入的参数需要使用系统预定义的,比如 Environment.DIRECTORY_PICTURES是图片目录,因为这样可以使这个文件夹里的文件与系统文件保持一致,当系统搜索外置存储的图片文件时,会优先扫描这个目录。

5.查询空闲空间

   查询空闲空间很简单,调用getFreeSpace()和getTotalSpace()可以分别获取空闲空间和总空间。
   尽管使用getFreeSpace()可以获取系统的空间空间,但是,系统并不保证你一定可以写入这么多的文件,当系统空间被使用的没达到90%,你可以放心写入文件,但是,一旦超过了90%,就会出现一些问题。比如说,你存储的文件明明比空闲空间要小,但是还是写不进去。
   Note:其实没有必要在每一次写入文件之前都检查空闲空间,直接写入文件也是可以的,一旦空间不足的话,会跑出IO异常,可以捕获异常,然后做异常处理。这么做的原因是有的时候,你并不知道你操作的文件有多大,比如说对文件转码,转完之后的大小是不知道的。

6.删除文件

   删文件的好处是可以提高存储空间的利用率,如果有一个文件,我们不需要了,就可以把它删了,以免浪费空间。
   删文件只需要调用文件对象本身的delete()方法就可以:

myFile.delete();

   如果文件存在内部存储中,也可以用context的deleteFile(String fileName)删除文件:

myContext.deleteFile(fileName);

Note: 卸载APP时系统会帮你删掉一些文件:

  • 内部存储的文件
  • 通过调用getExternalFilesDir()得到的目录下的文件
    但是,有一些文件还是需要我们手动删除,比如通过getCacheDir()创建的缓存文件。
0 0
原创粉丝点击