• 【Android Developers Training】 25. 保存文件


    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

    原文链接:http://developer.android.com/training/basics/data-storage/files.html


    Android使用的文件系统和其它平台上使用的磁盘文件系统比较类似。这节课将描述如何通过File的APIs对Android文件系统进行读写文件。

    一个文件对象适合于按既定的顺序读或者写大量数据,而非跳跃式地进行。例如,它适合于图像文件或者任何在网络上交换的数据。

    这堂课将展示如何在你的应用中,执行与基本文件相关的任务。我们假设你熟悉Linux文件系统基础知识,以及java.io中标准文件输入/输出流的APIs。

     

    一). 选择内存或外存

    所有Android设备有两个文件存储区域:“内部internal)”和“外部external)”存储。这两个名字的由来要追溯到早期的Android,那时大多数设备提供内置的非易失性存储器(内存),加上一个可移除的闪存介质,比如迷你SD卡(外存)。一些设备将永久存储空间划分为“内部”和“外部”两个部分,所以即使没有闪存介质,仍然会有两个存储空间。与此同时,不管外存是否是可移除的,对于API来说没有差异。下面将列举出每个存储空间的特性。

    内存:

    • 永远可以获取的到
    • 默认情况下,存储在这里的文件只有你的应用自身能获取到
    • 当用户卸载了你的应用,系统会从内存中删除所有该应用的相关文件

    综上所述,当你期望你的文件不会被用户或者其他应用获取时,内存将是最好的选择。

    外存:

    • 它并不能永远都可获得,因为用户可以将外存作为一个USB存储而挂载起来,并且在一些情况下会把它从设备上移除
    • 存储在这里的文件可被任意访问,而不在你的控制之内
    • 当用户删除了你的应用时,只有在你将文件存储在通过getExternalFilesDir()方法所得到的目录下时,系统才会删除你的文件

    综上所述,外存适合于存储那些对访问没有限制的文件,以及你希望和其他应用共享的文件,或者你希望用户可以通过电脑来获取到的文件。

    Tip:

    虽然默认情况下应用汇存储在内存,但你可以定义清单文件中的“android:installLocation”这一属性字段,这样你的应用就可以存储在外存上。当APK文件大小很大,同时用户拥有一个比内存空间要大的外存时,用户会期望能够这么做。更多信息可以阅读:App Install Location

     

    二). 获得操控外存的权限许可

    为了获得写入外存的权限,你必须在你的清单文件(manifest file)中声明“WRITE_EXTERNAL_STORAGE”的授权许可:

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

    Caution:

    目前,所有的应用都可以在没有特殊权限许可的情况下读取外存。然而这将会在未来的某个版本下改变。如果你的应用需要读取外存(而不需要写),那么你需要声明“READ_EXTERNAL_STORAGE”的权限许可,以此保证你的应用在未来版本更新以后还可以正常工作。务必在改变生效之前,现在就声明这个权限许可。

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

    然而,如果你的应用使用WRITE_EXTERNAL_STORAGE”的授权许可,那么它暗示了同时还拥有读外存的权限。

    在内存中保存文件不需要任何权限许可。你的应用永远都具有读和写在内存中其自身所对应的目录的权限。

    三). 在内存中保存一个文件

    当将一个文件存入内存时,你可以通过调用以下任一一种方法来取得合适的目录作为一个File对象:

    • getFilesDir():返回一个File对象,它代表了你的应用所拥有的内存中的一个目录。
    • getCacheDir():返回一个File对象,它代表了存放应用的临时缓存文件的内存目录。请确保删除每一个不再需要的文件,同时制定一个任何时刻你能使用的存储空间的大小限制,比如1MB。如果系统在运行时存储空间不足,它可能会在没有任何警告的情况下删除你的缓存文件。

    为了在上述任何一个目录中创建文件,你可以使用构造函数File(),传递给它上述两个方法中的一个来特定你的内存中的目录路径。例如:

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

    另外,你可以调用openFileOutput()来获得一个文件输出流,以此将数据写入你的内存目录中的一个文件。例如:

    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();
    }

    或者,如果你需要缓存一个文件,你应该使用createTempFile()方法。例如,下面的方法从一个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:

    你的应用的内存目录的位置是在Android文件系统中的一个特殊的位置,它由你的应用的包名所指定。从技术上来讲,如果你将文件的模式设置为可读,那么另一个应用是可以读取你的内部文件的。然而,这是在其他应用指导你的应用的包名以及相应的文件名的情况下。其他应用浏览你的内部目录并且没有读或写的权力,除非你显示地将文件设置为了可读或可写。所以只要你为你存放在内存中文件使用了MODE_PRIVATE标识,它们将永远无法被其他应用所访问到。

    四). 在外存上保存一个文件

    因为外存可能是无法获得的,比如当用户已经把存储挂载至PC上,或者已经移除了提供外存的SD卡。你应该在每次访问它之前先确认对应的卷是否存在。你可以通过调用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;
    }

    虽然外存可被用户或其他应用修改,但是你在这里存储的文件可以分为两类:

    公有文件(Public files):

    这些文件可以被用户和其他应用任意访问。当用户卸载了你的应用,这些文件将仍然保留。例如,你的应用所拍摄的图片或其他下载的文件。

    私有文件(Private files):

    属于你的应用的合法文件,用户卸载你的应用时,这些文件也会被同时删除。虽然从技术上说,因为这些文件存储于外存,所以它们可以被用户或其他应用访问到,但实际上这些文件在你的应用范围之外向用户提供任何数据。当用户卸载了你的应用时,系统会删除所有你的应用外部私有目录下的文件。此类文件的例子有:你应用所下载的额外的资源文件或者临时的多媒体文件。

    如果你希望在外存中存储公有文件,使用getExternalStoragePublicDirectory()来获得一个File对象,它代表了外存上一个合适的目录。这个方法接受一个参数,该参数指定了你希望存储的文件类型,这样它们就能与其他公有文件一起被统一地管理了,文件类型诸如:DIRECTORY_MUSIC或者DIRECTORY_PICTURES

    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;
    }

    如果你希望存储属于你应用的私有文件,那么你可以调用getExternalFilesDir()来获得一个合适的目录,同时传递给它一个名字来说明目录类型(如果你喜欢的话)。每个通过这种方式创建的目录会添加到一个父目录下,以此把你的应用的所有外存文件都封装起来。当用户卸载应用时,他们会被删除。

    例如,下面的方法可以用来为一个个人相册创建一个目录:

    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”。这样将会返回你应用在外存上的私有目录路径的根路劲位置。

    记住,getExternalFilesDir()所创建的目录是在一个当用户卸载你的应用时,会被一起删除的目录下的。如果你所保存的文件在用户卸载应用后仍然需要存在(比如你的应用是一个相机软件,用户希望保留这些相片),那么你应该使用getExternalStoragePublicDirectory()

    不管你使用的是getExternalStoragePublicDirectory()(用于共享的文件),还是getExternalFilesDir()(用于私有的文件),使用API常量提供的目录名(诸如DIRECTORY_PICTURES)是很重要的。这些目录名保证了系统会正确地处理这些文件。例如,存储于DIRECTORY_RINGTONES下的文件会被系统的多媒体扫描器分类为铃声而不是音乐。

    五). 查询空余空间

    如果你能提前知道你要存储多大的数据,那么你将知道是否有足够的空间来存储这些数据,从而避免IO异常(IOException)。通过调用getFreeSpace()或者getTotalSpace()这两个方法可以实现上述的预期。这两个方法分别提供了在存储卷内的当前可用空间和总空间。这些信息还可以用来避免向存储卷内填充超出阈值数量的数据。

    然而,系统不会保证你可以写入和getFreeSpace()所返回的可用空间一样大小的数据。如果返回的数量比你希望存储的数量多了几兆,或者文件系统的使用率小于90%,那么继续执行是没有问题的。否则你可能无法写入数据。

    Note:

    你不必在你存储文件之前检查可用空间的大小。你可以尝试直接写入文件,然后当异常发生时捕捉IOException。你需要这么做如果你不知道你具体需要多少空间。例如,如果你在保存之前转变了文件的编码(把PNG格式的图片转换为JPEG,此时你无法预知文件的大小)。

    六). 删除一个文件

    你应该将不再需要的文件删除。删除文件最直接的方法是对文件对象自身调用delete()

    myFile.delete();

    如果文件存储于内存,你也可以通过Context来定位,然后调用deleteFile()删除文件:

    myContext.deleteFile(fileName);

    Note:

    当用户卸载了你的应用,Android系统会删除如下文件:

    然而,你要定期手动地删除所有通过getCacheDir()创建的临时文件以及其它你不再需要的文件。

  • 相关阅读:
    sublime简单配置
    win10系统,找不到gpedit.msc文件怎么办?组策略编辑器添加方法
    VMware Workstation and Device/Credential Guard
    Centos修改镜像为国内的阿里云源
    wxparse 第三方组件的使用
    VMware Workstation 与 Device/Credential Guard 不兼容.在禁用 Device/Credenti
    session配合cookies免登录
    python中dump 和dumps load和loads的区别
    什么是进程、线程?
    电梯测试
  • 原文地址:https://www.cnblogs.com/jdneo/p/3464170.html
Copyright © 2020-2023  润新知