【专业技术】Android数据保存之文件保存

前言:

上一篇文章写了在Android中利用SharedPreferences保存数据,SharedPreferences在保存数据的时候主要是保存一些应用程序的设置信息或者少量的用户信息,并且是以key-value形式保存的String类的信息,比较有局限性。比如你需要保存从网络获取的图片到本地作为缓存数据,并且数量比较大,SharedPreferences就不能满足你的需求了,这个时候就要用到基本上所有平台都会用到的文件保存。

Android中以文件形式把数据保存到磁盘上与其他平台基本上都是类似的,本篇文章将会介绍如何利用java.io.Files的API函数进行文件的读写操作。

选择内部存储还是外部存储:

所有的Android设备有两个文件存储区域:“内部”和“外部”存储。这些名字来自Android的早期,那时大多数设备提供了内置的非易失性存储器(内存),加上一个可移动的存储介质如micro SD卡(外部存储)。现在的Android设备基本上内置的存储空间都很大,比如16g或者32g,这里的16g和32g是指的总共磁盘大小,相当于你新买的电脑一块崭新的硬盘。在手机出厂的时候会在这块磁盘上烧上android系统,android系统会把整个磁盘进行分区,一部分提供给android系统存放系统文件使用,类似windows的系统盘,但是要比windows上权限严格的多,用户是不能随意访问这部分文件的(root除外),这一部分叫做内部存储,剩余的部分用户可以自由使用,手机连上电脑时查看到的也只是这部分文件,叫做外部存储,相当于windows上的其他磁盘(比如D盘),当然有的用户又添加了一张micro-SD卡,这部分也算做外部存储,相当于windows上的外接硬盘吧。

内部存储和外部存储是有区别的,在利用的时候需要注意他们各自的特点:

内部存储:

  • 始终存在可用;
  • 保存的文件默认只能被保存文件的app访问,各个应用之间不可以彼此访问,只能访问自己保存的文件。
  • 当应用被卸载的时候应用保存的文件会被完全清除掉;
  • 如果你想要保存的文件很安全,不会被用户和其他应用读取到,那么你可以选择内部存储这种方式。

外部存储:

  • 不一定存在,比如有的手机出厂是只有内部存储,没有外部存储,用户自己又没有安装micro-SD卡,这时外部存储是不可用的;
  • 读写完全开放的,所以你保存的数据可能会被用户和可其它程序读取;
  • 卸载应用时只会删除通过getExternalFilesDir()获取到的目录文件;
  • 如果你的文件没有必要控制访问权限,可以允许其它应用或者用户查看,那么外部存储是不错的选择;

注:在默认情况下应用程序安装到内部存储,您可以指定android:installLocation属性在AndroidManifest.xml文件中,这样你的应用程序可以安装在外部存储器。对于用户来说有这个安装选项非常实用,当一些应用程序非常大,内部存储空间不足的时候用户可以把应用安装到外部存储空间。

获取外部存储权限:

要想在外部存储上存储文件首先要获取外部存储读写权限,权限的声明都是在AndroidManifest.xml文件中,代码如下:

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

注意:现在所有的应用程序默认都有外部存储的读取权限,你不需要在AndroidManifest.xml文件中进行声明,但是这种默认的权限可能会在以后的Android版本中变更,所以最好还是要在AndroidManifest中显式的进行读取权限声明,免得在以后的版本中程序出现问题,读取权限声明如下:

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

另外:

1、写入权限隐含就有读取权限;

2、内部存储不需要进行权限声明,应用程序对于内部存储默认就有读写文件的权限;

保存到内部存储:

文件存储需要创建文件,当把文件保存到内部存储时你可以获取内部存储文件通过下面的两个方法:

1、File getFilesDir ();

返回一个文件目录,这个目录下保存应用程序的数据,通过 openFileOutput(String, int) 创建的文件都保存在这个文件目录下。这个目录大概是:data/data/包名/files,比如豌豆荚应用程序是:data/data/com.wandoujia.phoenix2/files/

2、File getCacheDir ();

返回一个文件目录,这个目录存放的是应用程序缓存文件,当系统空间不足时这部分文件首先会被删除。这个目录大概是:data/data/包名/cache,比如豌豆荚应用程序是:data/data/com.wandoujia.phoenix2/cache/ 注意:缓存文件的删除不应该依赖系统去删除它,最好的办法是给你的应用缓存设置一个最大值,比如1M,当达到这个值时你应该去删除部分缓存文件以便能再次利用这部分空间。

当你想要在内部存储写入一个文件时,首先要创建一个文件,可以通过File的构造器,传入上面两个方法获取的路径作为参数,很方便的就能创建一个文件,例如:

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

然后再通过上面的file创建文件流,写入文件,当然你可能更喜欢下面的方式,通过调用 openFileOutput() 创建一个FileOutputStream ,然后写入文件,代码如下:

String filename ="myfile";
Stringstring="Hello world!";
FileOutputStream outputStream;

try{
  outputStream = openFileOutput(filename,Context.MODE_PRIVATE);
  outputStream.write(string.getBytes());
  outputStream.close();
}catch(Exception e){
  e.printStackTrace();
}

当你需要创建一个缓存文件时,你可以通过下面的方式:

File file = newFile(context.getCacheDir(), filename);

或者,你会更喜欢下面的方式,通过File的creatTempFile方法在cache目录创建临时文件,文件的后缀是.tmp:

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

注意:通常情况下你的应用程序内部存储文件是不会被其他应用程序访问到的,因为其他程序的访问首先需要知道你应用的包名和文件名,其次需要获取到你这个文件的访问权限。从技术上来说如果你存放的文件开放了文件读取权限其他应用程序就能读取到,除非是你把文件设置为可读写的,要不然其他程序是无法读取你的文件的,所以文件权限Context.MODE_PRIVATE是必须要设置的。

保存到外部存储:

保存到外部存储首先要检查外部存储是否存在并有剩余空间,因为外部存储有可能会被拔掉,或者正在连接着电脑,所以当你要在外部存储保存文件的第一步就是检查外部存储是否挂在,可以通过调用getExternalStorageState()方法来查看外置存储是否挂载,如果返回状态是Environment.MEDIA_MOUNTED,则表明已经挂在,并且可以读写。例如下面的代码:

/* Checks if external storage is available for read and write */
publicboolean isExternalStorageWritable(){
    String state =Environment.getExternalStorageState();
    if(Environment.MEDIA_MOUNTED.equals(state)){
        returntrue;
    }
    returnfalse;
}

/* Checks if external storage is available to at least read */
publicboolean isExternalStorageReadable(){
    String state =Environment.getExternalStorageState();
    if(Environment.MEDIA_MOUNTED.equals(state)||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)){
        returntrue;
    }
    returnfalse;
}

尽管外部存储的文件可以被用户和其他程序访问,但是对于外部存储的文件你需要分两类对待:

public files:

这类文件是完全开发的,对于其他应用程序或者用户都可以访问,当你的应用被卸载的时候这部分文件也不会被删除,比如你的拍照程序,用户拍的照片不会因为用户卸载了应用而删除照片,还比如看视频软件,用户下载下来的视频也不能因为卸载二删除。

private files:

这类文件属于你的应用程序专有,对于其他应用程序无法使用,也没有任何利用价值,虽然这部分文件对用户和其他程序是开放的。这类文件在应用卸载的时候应该被删掉,要不然会造成用户空间的浪费,比如一些缓存文件,地图资源等。如果你想保存一个公用的文件到外部存储,你可以通过Environment.java中的:

public static File getExternalStoragePublicDirectory (String type)方法获取外部存储的公共目录,公共目录有几种类型,根据你输入的type返回不同的文件夹,type类型有:

public static String

DIRECTORY_ALARMS

标准的铃声目录

public static String

DIRECTORY_DCIM

相机拍照或录像文件的存储目录

public static String

DIRECTORY_DOWNLOADS

下载目录

public static String

DIRECTORY_MOVIES

电影目录

public static String

DIRECTORY_MUSIC

音乐目录

public static String

DIRECTORY_NOTIFICATIONS

提示音目录

public static String

DIRECTORY_PICTURES

图片目录

public static String

DIRECTORY_PODCASTS

播客目录

public static String

DIRECTORY_RINGTONES

铃声目录

大家最常见的DIRECTORY_PICTURES目录是:/mnt/sdcard/Pictures,比如你想要存储一张图片,要在外部存放图片的公共目录创建一个图片文件:

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

如果你想要保存私有类型的数据到外部存储上,可以通过调用Context.java中的:

public abstract File getExternalFilesDir (String type) 方法获取外部存储路径,路径是:

/mnt/sdcard/Android/data/data/your_package/type type同上,根据你想要保存的文件类型选择,下面是创建存放私有图片文件的例子:

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

如果type中没有你需要的类型,你可以输入null,此时返回的是你的应用程序外部存储目录的私有目录的根目录。

注意:通过getExternalFilesDir(String type) 方法创建的文件在用户清除数据或者在应用卸载的时候会被系统清除掉,getExternalStoragePublicDirectory(String type) 方法创建的文件则不会。另外,无论你用哪一种方法创建应用程序外部存储文件,注意一下type类型的正确性,以便于系统处理的时候能够正确处理,比如你保存的一个文件是铃声类型,在DIRECTORY_RINGTONES下,系统MediaScanner在进行多媒体扫描的时候会把这个文件分类为铃声而不是音乐。

查询剩余空间:

如果你提前知道你要保存的文件大小,你就可以通过File.getFreeSpace()或者File.getTotalSpace()方法来估算存储空间是否能够容纳,这样就可以避免在没有足够的存储空间时出现IOException。然而有的时候通过File.getFreeSpace()获取的可用空间不一定就有那么多供你使用,如果通过File.getFreeSpace()获取的大小比你的文件大几M或者文件系统有大于10%的剩余空间,这时保存文件可能能够正常进行,否则可能就会保存失败。

注意:在你保存文件之前,你不需要检查可用空间,而是在写入文件的时候捕获IOException,用这种方法来代替空间大小的检查,如果你不知道你需要多少空间。

删除文件:

当你不再需要一个文件时你需要删除它,最直接的方法就是直接调用File.delete()方法来删除。如果这个文件被保存在内部存储上,你也可以调用Context.deleteFile(String name)方法类删除文件。

在用户卸载你的应用的时候Android系统会删除你的一下文件:

1、所有保存在内部存储的文件;

2、所有保存在getExternalFilesDir()目录的外部存储文件;

注意:你需要定期手动清理通过getCacheDir()缓存的文件和不再需要的文件。

总结:

以上讲解了Android系统中文件保存的相关知识,文件保存根据保存位置分为外部存储和内部存储,根据开放性和对应用程序的可用性分为私有类型和公有类型,还有文件保存的方法和一些注意事项

原文发布于微信公众号 - 程序员互动联盟(coder_online)

原文发表时间:2015-07-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Charlie's Road

Python文件读写保存操作

上面的代码其实没啥要介绍的,就是一个for循环然后逻辑和单个文件读操作一样,只是多了个写操作。

581
来自专栏Java帮帮-微信公众号-技术文章全总结

Maven 核心原理解析(2)

聚合与继承 Maven的聚合特性(aggregation)能够使项目的多个模块聚合在一起构建, 而继承特性(inheritance)能够帮助抽取各模块相同的依赖...

3747
来自专栏闵开慧

eclipse中hadoop2.3.0环境部署及在eclipse中直接提交mapreduce任务

1 eclipse中hadoop环境部署概览 eclipse中部署hadoop包括两大部分:hdfs环境部署和mapreduce任务执行环境部署。一般hdf...

3129
来自专栏皮振伟的专栏

[qemu][acpi]acpi overflow的分析

前言: qemu启动之后,打出了log: qemu-system-x86_64: Warning: ACPI tables are larger than 64...

3548
来自专栏青枫的专栏

Java的GUI窗体出现乱码解决方法

  用java做一个图形化界面的程序,在Eclipse上运行后中文显示乱码,如下图所示:

971
来自专栏Golang语言社区

轻度Linux服务器维护人员常用的Shell脚本命令

笔者平时属于非专业运维人员,但是偶尔也需要接触一些测试服务器和个人VPS服务器。所以收集了一些常用的Shell脚本,希望跟我一样的开发人员能有一些收获。 文件操...

42713
来自专栏我的博客

PHP敏感函数关闭参考

搜索disable_functions 然后改为=disable_functions=phpinfo,dl, exec, system,passthru,pop...

2998
来自专栏Golang语言社区

linux后台开发常用调试工具

一、编译阶段 nm 获取二进制文件包含的符号信息 strings 获取二进制文件包含的字符串常量 strip...

50214
来自专栏Java成神之路

JavaWeb_常用功能_01_文件上传

 一个功能完善的JavaWeb应用,必不可少的一个功能就是文件的上传。无论是用户的头像等,还是用户需要上传的一系列资料,都是通过文件的上传功能实现的。

763
来自专栏云计算教程系列

如何在CentOS 7上为Apache设置mod_rewrite

Apache是一个模块化Web服务器,允许您通过启用和禁用模块来自定义其功能。这使管理员能够定制Apache的功能以满足其Web应用程序的需求。

90

扫码关注云+社区