Android中的数据存储主要分为三种基本方法:
2.传统文件系统。
3.利用SQLite的数据库管理系统。
对SharedPreferences对象和SQLite数据库,它们的数据仅对创建它们的应用是可访问的。
换句话说,它们不是共享的,如果需要在不同的应用之间共享数据,需要建立content provider,本文对这部分内容不作介绍。
本文基本是官方文档翻译,原文请见文后链接。
选择内部或外部存储
获取外部存储设备的权限
向外部存储存储文件
三.在SQL数据库中存储文件
向数据库中存入数据
从数据库中读取数据
删除数据库中的信息
提供了一种存储键值对的方法,可以用于存储原生数据类型(boolean, float, int,
这些数据在用户工作阶段一直被保持,即便应用被关闭也还是保持。
所以可以用来保存一些数据,比如用户设定的字体,背景,用户名等,在下一次打开应用时,不用重新设定这些数据。
获得对象可以使用下面两种方法:
这是 中的方法,可以通过第一个参数指定你需要获得的文件名。
向Shared Preferences写数据,首先需要调用方法创建一个 对象,然后调用这个对象的putXXX()方法存储键值对,键都是String类型,值是XXX所对应的数据类型(boolean,
读取数据时需要调用SharedPreferences对象的getXXX()方法,来获得给定键(第一个参数)对应的值,如果给定键不存在,则会返回给定的默认值(第二个参数)。
Android文件系统和其他平台上的类似,使用 APIs可以读写文件。
这部分内容需要你已经了解了Linux文件系统的基础,并且也了解了包中的标准文件输入输出APIs。
所有的Android设备都有两块文件存储区域:内部和外部存储。
内部存贮一般指设备自带的非易失性存储器,外部存储指可拆卸的存储介质,比如微型的SD卡。
一些设备把永久的存储区域分为"internal"和"external"的分区,所以即便没有可拆卸的存储介质,这些设备永远都有两种存储区域,并且不管外部存储区到底是可拆卸的还是内置的,APIs的行为是一致的。
存储在内部存储区域的数据默认情况下只对你的app可用。无论是用户或者是其他app都不能访问你的数据。
当用户卸载你的app时,系统会自动移除app在内部存储上的所有文件。
不一定一直可以访问,因为用户可以拆卸外部存储设备。
存储在外部存储的文件是全局可读的,没有访问限制,不受你的控制。可以和其他apps分享数据,用户使用电脑也可以访问在外部存储中的文件。
当用户卸载你的app时,只有当你把文件存储在以 .获得的路径下时,系统才会帮你自动移除。
注意:默认情况下app是安装在内存上的,可以通过在manifest中指定 属性来安排app的安装位置。具体见.
获取外部存储设备的权限
为了向外部存储中写入数据,需要在manifest中指定权限 :
注意:现在所有的app都可以读取外部存储中的数据,而不需要特殊的权限说明。
但是,这点在新版本的更新中有可能会改变,如果你的应用需要读取外部存储中的数据而不需要写数据,应该声明 权限。
为了保证应用能够持续工作,应该现在开始就加入读取权限:
然而,如果你的应用包含了 权限,它隐式地包含了读取权限。
向内部存储存文件时,可以通过下面两个方法获取合适的路径,返回值是一个File对象。
返回一个File,表示的是app的应用文件在内部存储的绝对路径。
返回一个File,表示的是app的缓存文件在内部存储的绝对路径。
在如上路径中创建一个新文件,可以利用 的构造方法。将上面两个方法获得的File对象作为参数传入,如:
另外,也可以调用 来获取 ,然后向你的内部目录写入数据,如下:
或者,如果你需要缓存一些文件,你应该使用.
比如,下面的例子从一个URL中提取了文件名,然后利用该文件名创建了一个文件存储在应用的内部缓存路径下:
你应用的内部存储路径是由应用的包名指定的,在Android文件系统上的一个特定位置。
技术上来说,如果你把文件的模式设置为可读的,其他应用是可以读取你的内部文件的,但是,另外的应用需要知道你的应用的包名和文件的名字。
如果不明确指明,其他应用是不可浏览你的内部文件路径的,也不拥有读取和写入的权限。
所以,只要你对内部存储上的文件使用,它们对其他应用来说就是不可用的。
因为外部存储很有可能不可用,所以每次使用前都需要检查可用性。
通过方法可以查询外部存储状态,如果返回状态为, 你可以继续对文件进行读写操作。
尽管外部存储可以被用户和其他app修改,仍然有两种类型的文件你可以选择:
这些文件对用户和其他应用都是可用的。当用户卸载应用时,这些文件对用户仍然是可用的。比如,应用拍摄的照片文件或者是下载的文件。
属于你应用的文件,应当在应用被卸载的时候同时被删除。
尽管从技术上来说,这些文件可以被用户和其他文件访问,因为它们是存储在外部存储介质上的,但是它们在你的应用外部并不提供什么实际价值。当用户卸载应用时,系统会删除外部存储上应用私有路径下的所有文件。
比如,应用下载的一些额外的资源或者临时的媒体文件。
存储公有文件,首先用获得路径,这个方法需要一个参数指明文件类型,比如 或 . 如:
如果想要创建私有文件,利用 方法获得路径,并且传递给它一个名字指明路径类型。
每一个用这种方法创建的路径都会被加在一个父目录中,这个目录包装了你的应用的所有外部文件,当你卸载应用时,系统会删除它们。
比如,下面的方法为相册创建了一个路径:
如果所有预定义的子目录都不适合于你的文件,你可以调用并且传入null。这样会返回你的应用在外部存储上私有路径的根目录。
要记住在一个卸载应用时系统要删除的目录中创建目录。如果你的文件在应用删除之后仍需要保存,你应该使用。
无论使用哪种方法,比较重要的一点是,要使用API提供的路径名常量,如等。这些路径名保证了文件会被系统正确处理。
如果你提前知道你要存储多少数据,你可以实现查询是否有足够的存储空间,而不必引起一个 。
通过调用 和方法,你可以获取当前的空闲空间和当前卷的总空间。
但是,系统并不保证你可以存储的容量和 方法获取的字节数一样多,如果该方法返回的容量要比你实际存储的数据大小多几个MB,或者存储后系统的填满程度小于90%,那么很可能是可以安全处理的。否则,可能就存储不下了。
并没有要求你必须先检查剩余空间再存储文件,你可以把存储的语句写入一个try块中,然后catch IOException。在你并不知道文件多大时你需要这么做。
在你不需要文件时你需要将其删除。
最直接的方法:获得文件引用然后调用 方法:
如果文件存储在内部存储上,可以请求 去定位和删除文件,通过调用:
注意,当用户卸载你的app时,Android系统删除如下:
所有在内部存储上的文件。
所有用存储在外部存储上的文件。
然而,你需要定期手工地删除利用 创建的所有缓存文件。并且,需要定期清除所有不再需要的文件。
对于重复或结构性强的数据来说,把它们存储在数据库中是一种理想的做法。
Android系统上你将会用到的数据库相关的APIs都在这个包中: 。
SQL数据库中的主要原则之一就是构架(schema):一个关于这个数据库是如何组织的一个正式的声明。
构架反映在创建SQL数据库的语句中。
你可能会发现创建一个同伴类(companion class)很有用,同伴类同时被称作合约类(contract class),其中明确规定了你的构架的布局,以一种系统且自说明的方式。
一个合约类(contract class)是一个常量的容器,这些常量定义了URI,表的名字,列的名字。
合约类允许你在同一个包的其他类中使用这些名字常量。
这就允许了你在一个地方改变列名,而同时把它传播到代码的其他地方去。
组织合约类的一个好方法是:把对于你的整个数据库来说是全局的那些定义放在类的根级上;然后对于每一个表(table)创建一个内部类,列举其列。
注意:通过实现 接口,你的内部类要继承一个基本的关键字域叫做_ID,一些Android的类比如cursor
它不是必须的,但是它能帮助你的数据库更和谐地和Android framework工作。
比如,下面这个代码段定义了表名和列名:
为了阻止不小心实例化合约类,给它一个私有的空构造函数:
一旦你定义好了你的数据库看起来什么样,你就应该实现一些方法,用来创建和维护数据库和数据表。
下面是创建和删除表的一些典型的语句:
就好像你存储文件到设备的内部存储上一样,Android在你应用相关的私有磁盘空间上存储你的数据库数据。
你的数据是安全的,因为默认情况下其他应用无法访问这块区域。
类提供了一些有用的APIs,当你使用这个类来获取数据库的引用时,系统仅在需要时执行可能长时间的操作:创建和更新数据库,而不是在应用启动的时候执行。
你需要做的仅仅是调用 或方法。
注意:因为它们是长时间运行的,所以请确保你在背景线程中调用:
为了使用,创建一个子类,然后覆盖这三个回调函数:
你也可以实现 ,但这并不是必须的。
如下是一个对的实现例子:
要访问你的数据库,需要先实例化你的的子类。
想数据库中插入数据:通过向 方法中传入一个 对象实现。
方法接收的第一个参数是表的名字,第二个参数是列的名字:可以通过这个参数设定一个列名,如果是空值,这样这个列就会插入NULL;如果第二个参数是null,那么如果是空值将不会被插入表中。
从数据库中读取,是通过方法,向它传递你的选择标准和你想要的列。
这个方法结合了和方法的元素,只不过它的列的表指定了你想取出的数据,而不是插入的数据。
查询结果是由一个对象的形式返回给你的。
想要看一个Cursor中的一行,可以使用 类中的各种move方法中的一个,当你开始读取值时你必须先调用它。
一般情况下,你应该先调用方法,它将把读取位置指向结果中的第一项。
对每一行,你可以通过Cursor类的get方法读取各列内容,比如 或 。
对每一个get方法,你必须指定你想要的列的索引,你可以通过 或 方法来获得索引。
要删除表中的行,你需要提供一定的选择标准来确定要删除的行。
数据库API提供了一种机制,用于创建选择标准,防止SQL注入。
该机制将选择标准分为一个选择从句和一些选择参数。这个从句定义了需要查看的列,也允许你结合列的测试。这些参数是和从句绑定的需要测试的值。
因为结果不像常规的SQL语句那样处理,所以它是防SQL注入的。
当你需要修改你的数据库中值的一个子集时,运用方法。
更新数据表结合了方法中的content