用Preferences API 存储对象
来源:互联网 发布:海岛奇兵火箭炮手数据 编辑:程序博客网 时间:2024/05/18 05:01
Preferences API 是轻型的、跨平台的持久性 API,它是在 JDK1.4中引入的。它不是为了为传统数据库引擎提供一个接口,而是用恰当的、操作系统特定的后端以实现真正的持久性。这个 API是用来存储少量数据的。事实上,它的名字本身就表明它通常用于存储用户特定的设置或者首选项,如字体大小或者窗口布局(当然,您可以在其中存储任何您想要存储的内容)。
Preferences API 设计为存储字符串、数字、布尔值、简单字节数组等。在本文中,我们将为您展示如何用 PreferencesAPI存储对象,并提供了一个为您处理细节的工作库。如果您的数据可以容易地表示为简单对象而不是像字符串和数字这种分离的值时,它会很有用。
我们首先对该 API 作一简短讨论,包括一些使用它的简单例子,然后详细讨论如何使用这个 API存储对象,并给出为我们完成这项工作的代码。我们还展示了一些使用这个 API 的例子。
为什么设计 PreferencesAPI?
如果说 Preferences API 主要是为让 Java 程序访问 Microsoft Windows注册表而创建的,一定会让人感到意外。为什么我要这么说呢?这个 API 的设计类似于 Windows注册表,本文前三段中的大部分说明也同样适用于注册表。
不过,Preferences API 就像所有 Java 语言一样,是以跨平台为目的的,所以它在非 Windows系统上至少可以工作得一样好(当然,本文中的代码是跨平台的)。
Preferences API 规范没有规定如何实现这个 API,只规定了它必须做什么。Java 运行时环境(Java RuntimeEnvironment JRE)的每一个实现对这个 API 都可以有不同的实现。许多非注册表的实现将 API 数据存储在一个 XML格式的文件中,这个文件也许是在用户的主目录中或者在一个共享目录中。
与 Windows 注册表一样,Preferences API使用层次树结构来存储数据。起始点是一个
我们将首先简单看一下 Preferences API 是如何工作的以及如何使用它。
回页首
使用Preferences
理解 Preferences API 的最好方法是使用它。需要做的第一件事是访问根节点:
Preferences root = Preferences.userRoot();
这一行代码返回数据树的
这两个树天生就有不同的目的。您要将字体首选项存储在用户树中,因为这是用户特定的内容。另一方面,您要将程序位置存储在系统树中,因为位置对于所有用户是相同的,并且所有用户都可能用到它。
小型程序会使用系统树或者用户树,但是不会同时使用这两者。大型应用程序可能同时使用这两种树。在本文中,我们将只针对用户树,要记住用户和系统树的行为是一样的。
现在让我们看一下如何用 Preferences API 读取和写入简单的值。
回页首
获得一个值
当您得到根节点后,就用它读取和写入值。下面是如何写入一个字体大小:
root.putInt( "fontsize", 10 );
下面是在这之后将它读出来的方法:
int fontSize = prefs.getInt( "fontsize", 12 );
注意 getInt()
当然,您可以读取和写入整数之外的值。可以读取和写入许多基本 Java 类型。还可以将节点存储在其他节点中,如这个例子所示:
Preferences child = parent.node( "child" );
这就是 Preferences API 的全部内容 -- 剩下的就是细节使用了,我们将在下一节讨论其中一个细节。
回页首
获得一个包的节点
不难想像两个不同的程序员可能希望存储不同的字体大小,如果他们决定以同一个名字“fontsize”存储他们的值,那么我们就有问题了。一个程序的首选项会影响另一个程序。
解决方法是将内容存储在包特定的位置上,像这样:
Preferences ourRoot = Preferences.userNodeForPackage( getClass() );
userNodeForPackage()
Class
对于 Preferences API 的工作方式有了很好的了解后,我们还需要知道如何扩展它以便对对象进行处理。
回页首
存储对象
这就是我们希望将对象写入 Preferences 树的理想方法:
清单 1. 将对象写入 Preferences 树的理想方法
Font font = new Font( ... );Preferences prefs = Preferences.userNodeForPackage( getClass() );prefs.putObject( "font", font );
不过,不幸的是,Preferences 对象没有 putObject()
getObject()
PrefObj
清单 2. 实现 putObject() 和 getObject()
Font font = new Font( ... );Preferences prefs = Preferences.userNodeForPackage( getClass() );PrefObj.putObject( prefs, "font", font );
我们已经尽量做到在 Preferences
下一节,我们将看一看 getObject()
putObject()
回页首
将对象转换为字节数组
我们在这里使用的技术用到了两个技巧。第一个技巧是将对象转变为一个字节数组。这样做的原因很简单:尽管 Preferences对象不处理对象,但是它可以处理字节数组。
幸运的是,我们不需要从头开始 -- 它已经建立在 Java语言中了。有几种方式将对象转换为字节数组,下面展示了我们在 PrefObj
清单 3. 将对象转换为字节数组
static private byte[] object2Bytes( Object o ) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream( baos ); oos.writeObject( o ); return baos.toByteArray();}
这里的关键是 ObjectOutputStream
ObjectOutputStream
ByteArrayOutputStream
还有一种使用其他方式的方法:
清单 4. 将字节数组转换为对象
static private Object bytes2Object( byte raw[] ) throws IOException, ClassNotFoundException { ByteArrayInputStream bais = new ByteArrayInputStream( raw ); ObjectInputStream ois = new ObjectInputStream( bais ); Object o = ois.readObject(); return o;}
一定要记 ObjectOutputStream
正如我在前面提到的,Preferences API的确可以对字节数组进行处理。不过,我们在这里构造的字节数组并不是很正确,我们将在下一节看到这一点。
回页首
将对象拆分为片段
Preferences API 对可以存储在它里面的数据大小有限制。具体就是字符串限制为 MAX_VALUE_LENGTH字符。字节数组限制为 MAX_VALUE_LENGTH 长度 75%,因为字节数组是通过编码为字符串存储的。
另一方面,一个对象可以为任意大小,所以我们需要将它分为几部分。当然,最容易的方法是首先将它转换为一个字节数组,然后将字节数组拆开。下面是拆开字节数组的代码,它也来自于 PrefObj
清单 5. 将字节数组拆分为可消化的大小
static private byte[][] breakIntoPieces( byte raw[] ) { int numPieces = (raw.length + pieceLength - 1) / pieceLength; byte pieces[][] = new byte[numPieces][]; for (int i=0; i<numPieces; ++i) { int startByte = i * pieceLength; int endByte = startByte + pieceLength; if (endByte > raw.length) endByte = raw.length; int length = endByte - startByte; pieces[i] = new byte[length]; System.arraycopy( raw, startByte, pieces[i], 0, length ); } return pieces;}
这里没有什么复杂的内容 -- 我们只是创建一个数组的数组,每一个长度为最大 pieceLength的字节长度(pieceLength是 MAX_VALUE_LENGTH 的3/4)。相应地,有另一种方法将各个部分再合并到一起:
清单 6. 将片段重新组装为完整的字节数组
static private byte[] combinePieces( byte pieces[][] ) { int length = 0; for (int i=0; i<pieces.length; ++i) { length += pieces[i].length; } byte raw[] = new byte[length]; int cursor = 0; for (int i=0; i>pieces.length; ++i) { System.arraycopy( pieces[i], 0, raw, cursor, pieces[i].length ); cursor += pieces[i].length; } return raw;}
这个例程检查所有片段的总长度并创建一个具有这种长度的新数组。然后将片段一个一个地拷贝进去。
回页首
读取和写入片段
这里我们使用第二个技巧 -- 将值转换为节点。一般来说,当我们用 Preferences API存储值时,我们将它放到首选项数据树中一个节点的 slot 中。
但是我们不能在这里真的这样做。即使一个对象只有一个值,我们也要将它转换为一组固定长度的字节数组。如果我们只有一个字节数组,写入数据树中的slot 会很容易,因为 Preferences API 直接支持字节数组。但是这行不通,因为我们有多个数组。
技巧是为每一个对象分配一个节点。让我们弄清楚它的意义。
通常,将值存储在节点的多个 slot 的其中之一。但是我们准备为每一个对象创建一个节点, 并将字节数组存储到该节点的 slot中。让我们说的更具体一些。如果可以,我们会将一个对象存储到单个 slot 中:
清单 7. 将一个对象存储到单个 slot 中
Preferences parent = ....;parent.putObject( "child", object );
但是我们不能这么做,因为 Preferences 没有 putObject()
清单 8. 将字节数组存储到一个节点中
Preferences parent = ....;Preferences child = parent.node( "child" );for (int i=0; i<pieces.length; ++i) { child.putByteArray( ""+i, pieces[i] );}
这样,不是将一个值存储到一个称为“child”的 slot 中,我们将几个值存储到一个称为 “child”的节点中。这些值是用数字键-- “0”、“1”、“2”等存储的。
使用数字键可以使后面读取片段时更容易:
清单 9. 读取容易读的片段
Preferences parent = ....;Preferences child = parent.node( "child" );for (int i=0; i<numPieces; ++i) { pieces[i] = child.getByteArray( ""+i, null );}
在下一节,我们将看一下结合所有这些步骤的例程。
回页首
将所有内容合到一起
PrefObjs
putObject()
清单 10. 方法 putObject() 使用其他方法来写入片段
static public void putObject( Preferences prefs, String key, Object o ) throws IOException, BackingStoreException, ClassNotFoundException { byte raw[] = object2Bytes( o ); byte pieces[][] = breakIntoPieces( raw ); writePieces( prefs, key, pieces );}
方法 putObject()
有一个用于读取的类似方法:
清单 11. 方法 getObject() 对写入片段做同样的事情
static public Object getObject( Preferences prefs, String key ) throws IOException, BackingStoreException, ClassNotFoundException { byte pieces[][] = readPieces( prefs, key ); byte raw[] = combinePieces( pieces ); Object o = bytes2Object( raw ); return o;}
这个方法从 Preferences API 中读取片段,并将它们结合为单个字节数组,然后将它转换为对象。
回页首
存储信息
正如您所看到的,这是一种使用 Preferences API所具有的功能的简洁方式,实现了它
参考资料
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文. - 下载本文所使用的
源代码。 - “
EncryptedPreferences in Java”主要讨论 Preferences API的集成加密技术(该站点需要注册)。 - “
ThePreferences API in Java 2SE 1.4”给出了该规范的一个概述。. - “
Usingthe Preferences API”提供了使用 API 的一个技巧示例。 - “
Magicwith Merlin: Working withpreferences”( developerWorks,2001 年10 月)展示了 Preferences API规范如何通过提供对一个实现特定的注册表的访问,让用户操纵用户首选项数据和配置数据。 - Java Community Process Program 提供了关于
JSR 10: The Preferences APIspecification 的细节,这个简单的 API让程序可以操纵用户首选项数据和配置数据,从版本 1.4.1 开始可用。 - Client-side Java programming forum
讨论一些与Preferences API 有关的话题,包括 Windows 注册表问题和 LDAP中的支持。 - PreferencesAPI站点提供了对这个包的概述,将它与其他类似的机制进行比较,并提供有关其使用说明和一个为什么它这样设计的常见问题。
- jGuru 以代码的方式对问题“
Can you show me a smallsnippet of how to use Preferences API inJDK1.4?”给予了快速答复。 - 本文作者所著的
JDK 1.4Tutorial 一书(Manning 出版社,2002 年)第 10章“ ThePreferences API”(以 PDF形式提供)展示了跨平台首选项数据的存储,包括改变监听器。 - 有关本主题的一个有价值的资源是 David Flanagan 的
Java in aNutshell, 4th Edition (O'Reilly& Associates,2002 年)。 - 在
developerWorksJava技术专区 上可以找到数百篇关于 Java各个方面的文章。
Returns the byte array value represented by the stringassociated with the specified key in this preference node.Valid strings areBase64 encodedbinary data, as defined in RFC 2045, Section 6.8,with one minor change: the string must consist solely of charactersfrom theBase64 Alphabet; no newline characters orextraneous characters are permitted. This method is intended foruse in conjunction withputByteArray
.
Returns the specified default if there is no value associatedwith the key, the backing store is inaccessible, or if theassociated value is not a valid Base64 encoded byte array (asdefined above). 但是在android2.2之前无法直接使用Base64,所以返回值总是为空
如果不考虑到分段:
private void saveObject(String key, Object obj) {
- 用 Preferences API 存储对象
- 用 Preferences API 存储对象
- 用 Preferences API 存储对象
- 用Preferences API 存储对象
- Preferences存储数据
- 数据存储【Shared Preferences
- 数据存储之Preferences
- Preferences Android数据存储
- 第五讲 用Shared Preferences方式存储数据
- 使用Shared Preferences存储数据
- 【Android数据存储】- Shared Preferences
- Shared preferences - Android 数据存储
- 使用Shared Preferences存储数据
- Swift对象存储API概述
- Preferences...
- pReferences
- Android数据存储——Shared Preferences
- 【Android】数据存储之Shared Preferences
- Creating Mutable and Immutable StringObjects
- stlport5.2.1编译
- ACCESSING THE CLOUD FROM COCOA TOUCH
- Consuming XML Web Services in iPhone Applications
- java_xml读取
- 用Preferences API 存储对象
- 解决序列化中的问题java.io.StreamCorruptedException: invalid stream header:EFBFBDEF
- vim常用命令之多行注释和多行删除
- NSArray and KVC (muti-value per key)
- Erlang 函数(Efficiency Guide)
- NSString copy or retain
- KVO
- Set to nil in viewDidUnload, but release in dealloc
- OC遍历属性