前言
对一款人性化的应用来说,往往需要存储用户的偏好设置,例如字体大小、应用风格等个性化的设置数据。Java应用具有跨平台的特性,就更需要一种平台无关的存储手段。Java官方给出的解决方案是Preferences
首选项,本文就来简单讲解一下Preferences
的使用。
Preferences的存储模式
首先,我们需要知道Preferences
为什么可以实现跨平台存储数据。实际上,在不同的操作系统中,Preferences
底层的存储方式是不一样的。例如,在Windows
下是将数据存储在注册表中,而在Linux
下则是将数据存储在文件系统中的。当然,对于程序员来说,这些都是透明的。
在Preferences
中,使用了树状结构来进行数据的存储,这主要是为了避免文件名冲突。我们需要知道的是,Preferences
中有着两棵独立的树。一棵是用户首选项树,一棵是系统首选项树。在用户首选项树中,一般存储应用程序的个性化设置,如字体大小、字体颜色等。而系统首选项树是全局共享的,一般用来设置应用程序的配置数据。通常我们使用用户首选项树即可。
在每棵首选项树中,存在着许多节点,我们的数据就存储在这些节点中。每个节点都有一个节点名,并且节点名可以不唯一,但是节点名中不可以包含“/”
字符。同时,每个节点都有一个绝对路径名,这是唯一的。首选项树的根节点绝对路径名是“/”
,它的子节点则是“/”+子节点名
,其他节点的绝对路径命名方式与此类似。对根节点之外的任何一个节点来说,其绝对路径都是父节点的绝对路径+“/”+当前节点名
。在每个节点对应的Preferences
对象中,使用键值对的方式来存储和获取数据,这类似于HashMap
的使用方式。
基本操作
获取Preferences对象
首先我们需要获取一个Preferences
对象。Preferences
提供了两个静态方法用于获取首选项树的根节点。
//获取用户首选项树根节点
public static Preferences userRoot()
//获取系统首选项树根节点
public static Preferences systemRoot()
得到根节点后,就可以通过node
方法获取我们需要的子节点了。node
方法原型如下:
public abstract Preferences node(String pathName);
对于node
方法,我们可以传入节点的相对路径名或绝对路径名。以“/”
字符开头的就是绝对路径名,而不以“/”
开头的则是相对于当前节点的相对路径名。如果该节点不存在,系统将会创建该节点,否则就返回已存在的节点。接下来我们尝试以当前包名作为路径获取一个子节点对应的Preferences
对象:
public class PreferencesDemo {
public static void main(String[] args) {
String absolutePath="/com/example/preferences";
Preferences preferences=Preferences.userRoot().node(absolutePath);
System.out.println(preferences);
}
}
输出结果:
User Preference Node: /com/example/preferences
可以看到,通过将当前包名作为绝对路径,我们已经成功获得了一个Preferences
对象。接下来,我们就可以通过这个对象写入和读取数据了。
写入数据/读取数据
Preferences
提供了一系列putXXX
方法用于写入不同类型的数据,方法原型如下:
abstract void put(String key, String value)
abstract void putBoolean(String key, boolean value)
abstract void putByteArray(String key, byte[] value)
abstract void putDouble(String key, double value)
abstract void putFloat(String key, float value)
abstract void putInt(String key, int value)
abstract void putLong(String key, long value)
相应的,Preferences
也提供了一系列getXXX
方法读取不同类型的数据,方法原型如下:
abstract String get(String key, String def)
abstract boolean getBoolean(String key, boolean def)
abstract byte[] getByteArray(String key, byte[] def)
abstract double getDouble(String key, double def)
abstract float getFloat(String key, float def)
abstract int getInt(String key, int def)
abstract long getLong(String key, long def)
需要注意的是,所有的getXXX
方法都需要传入一个默认值,这是为了在首选项不可用时依旧不会影响应用的正常运行,有利于提高程序的鲁棒性。以下演示向首选项写入和读取字符串:
String defStr=preferences.get("key_str","默认值");
preferences.put("key_str","新的值");
String newStr=preferences.get("key_str","默认值");
System.out.println(defStr);
System.out.println(newStr);
输出结果:
默认值
新的值
刷新数据
在Preferences
中,默认使用异步的方式写入数据,某些时候我们希望数据可以马上更新,就需要对数据进行刷新操作。这时,就可以使用flush
这个方法,其原型如下:
abstract void flush()
删除数据
Preferences
提供了两个删除数据的方法。remove
用于删除指定键对应的值,removeNode
则用于删除当前节点。这两个方法的原型如下:
abstract void remove(String key)
abstract void removeNode()
导入/导出首选项数据
某些时候,我们希望从外部导入应用的偏好设置,或者将当前应用的偏好设置导出进行备份。这时我们就可以使用Preferences
提供的导入/导出方法。在这个过程中,数据的载体是XML
文件。
static void importPreferences(InputStream is)
abstract void exportNode(OutputStream os)
abstract void exportSubtree(OutputStream os)
importPreferences
用于将XML
文件中的数据导入首选项系统。exportNode
用于导出当前节点存储的数据,但是并不会导出当前节点的子节点中的数据。exportSubtree
则会导出当前节点及其子节点的所有数据。以下演示如何将当前节点的数据导出到文件中:
public class PreferencesDemo {
public static void main(String[] args) {
.........
try {
FileOutputStream outStream=new FileOutputStream("preferences.xml");
preferences.exportNode(outStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (BackingStoreException e) {
e.printStackTrace();
}
}
}
以上程序会将当前节点的数据导出到项目根目录下的“preferences.xml”
文件中,文件内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<preferences EXTERNAL_XML_VERSION="1.0">
<root type="user">
<map/>
<node name="com">
<map/>
<node name="example">
<map/>
<node name="preferences">
<map>
<entry key="key_str" value="新的值"/>
</map>
</node>
</node>
</node>
</root>
</preferences>
一个典型的例子
接下来,我们演示一个Preferences
的使用场景。在程序首次运行时,将会输出欢迎语句,但是当程序再次运行时,将会输出其他内容。
public class PreferencesDemo {
public static final String KEY_FIRST_RUN="is_first_run";
public static void main(String[] args) {
String absolutePath="/com/example/preferences";
Preferences preferences=Preferences.userRoot().node(absolutePath);
//判断程序是否是第一次运行
boolean isFirstRun=preferences.getBoolean(KEY_FIRST_RUN,true);
if(isFirstRun){
System.out.println("欢迎使用本程序!");
preferences.putBoolean(KEY_FIRST_RUN, false);
}else{
System.out.println("这已经不是一次运行了哦~");
}
}
}
第一次运行:
欢迎使用本程序!
第二次运行:
这已经不是一次运行了哦~