【Android Developers Training】 25. 保存文件

系统 1658 0

注:本文翻译自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() 创建的临时文件以及其它你不再需要的文件。

【Android Developers Training】 25. 保存文件


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论