从JAR中保存首选项

时间:2013-05-18 18:35:15

标签: java

我目前正在编写游戏,并且已经到了我需要用户能够保存诸如JFrame大小,键绑定等内容的首选项的位置。用户将通过下载的jar运行游戏来自我的网站。

我决定使用“java.util.prefs.Preferences”下的Preferences API。在阅读文档等内容时,我仍然很新,并希望有人帮我解释一下。

到目前为止,我有这样的事情:

Preferences prefs = Preferences.userNodeForPackage(com.custardgames.horde2d.Game.class);
prefs.put(name, value);

现在,我如何实际在用户计算机上保存首选项文件?现在,如果我再次运行该程序并尝试:

Preferences prefs = Preferences.userNodeForPackage(com.custardgames.horde2d.Game.class);
System.out.println(prefs.get(name, null));

只返回null,如果没有保存,则返回默认值。我知道我错过了实际保存文件的重要部分,然后以正确的方式打开它,但谷歌搜索没有得到太多。

提前非常感谢!

编辑: 仅供参考,为了阅读图像,我使用的是这样的东西:

BufferedImage currentImage=ImageIO.read(this.getClass().getClassLoader().getResource("res/images/image.jpg"));

我希望能够做一些与偏好类似的事情。

编辑: namevalue都是事先声明的String变量。

2 个答案:

答案 0 :(得分:2)

您确定name变量具有相同的值吗?因为它应该返回正确的存储值 - 也许值为null?

尝试在存储之前将它们打印出来。得到。

您还可以尝试在存储后刷新首选项:

prefs.flush();

如果您使用的是Windows,则可以检查首选项是否存储在<{p}下的regedit

\HKEY_CURRENT_USER\Software\JavaSoft\Prefs\<your package path>
Linux中的

我不确定你的主目录中应该有一个隐藏文件夹。像

这样的东西
~/.java/.prefs/<package path>

Regedit preferences

我开发了一个JFrame,使用首选项存储最后一个位置&amp;框架的大小。你可以在这里看一下:

RememberableFrame

图书馆位于: java-utils

您可能感兴趣的代码段:

    public void saveSize() {
            Preferences preferences = Preferences.userNodeForPackage(this.getClass());
            preferences.put(getId() + X_KEY, String.valueOf(getLocation().x));
            preferences.put(getId() + Y_KEY, String.valueOf(getLocation().y));
            preferences.put(getId() + W_KEY, String.valueOf(getSize().width));
            preferences.put(getId() + H_KEY, String.valueOf(getSize().height));
            preferences.put(getId() + MAX_KEY,
                            String.valueOf((getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH));
            try {
                    preferences.flush();
            }
            catch(BackingStoreException e) {
                    e.printStackTrace();
            }
    }

    public void setSavedSize() {
            Preferences preferences = Preferences.userNodeForPackage(this.getClass());
            String xs = preferences.get(getId() + X_KEY, "");
            String ys = preferences.get(getId() + Y_KEY, "");
            String ws = preferences.get(getId() + W_KEY, "");
            String hs = preferences.get(getId() + H_KEY, "");
            String max = preferences.get(getId() + MAX_KEY, "");

            if(max != null && !max.trim().isEmpty() && Boolean.valueOf(max) == true) {
                    setDefaultSize();
                    setExtendedState(JFrame.MAXIMIZED_BOTH);
                    return;
            }

            if(xs.length() == 0 || ys.length() == 0 || ws.length() == 0 || hs.length() == 0) {
                    setDefaultSize();
            }
            else {
                    sizeFromPreferences = true;
                    int x = Integer.parseInt(xs);
                    int y = Integer.parseInt(ys);
                    int w = Integer.parseInt(ws);
                    int h = Integer.parseInt(hs);
                    setLocation(x, y);
                    setSize(w, h);
            }
    }

修改

如果要创建某种设置存储系统,可以使用与RememberableFrame中相同的约定 - 使用前缀。

RememberableFrame我使用:

    private String getId() {
            return this.getClass().getSimpleName() + id;
    }

其中id是开发人员提供的自定义字符串或空字符串。但请记住,您要设置的属性的键具有长度限制。

来自API:

static int  MAX_KEY_LENGTH 
      Maximum length of string allowed as a key (80 characters).
static int  MAX_NAME_LENGTH 
      Maximum length of a node name (80 characters).
static int  MAX_VALUE_LENGTH 
      Maximum length of string allowed as a value (8192 characters).

您也可以考虑专门用于“存储设置”目的。例如,类KeyboardSettings,甚至在不同的包中。

答案 1 :(得分:1)

作为替代方案,我个人更喜欢保存配置文件(并避免使用注册表),Properties类可以很好地实现。就像您想象的那样,您通常会在用户计算机上的逻辑位置创建一个文件,通常是应用程序启动的目录或用户的主目录。

jGrep,我刚才写的一个Swing应用就是这样做的,欢迎您使用相关的代码作为起点。源代码是公共Mercurial存储库jGrep source code,相关行以JGrep.java:140开头。

基本上,你做了三个步骤(下面的代码片段基于jGrep代码库):

  1. 决定保存文件的位置:

    jGrep既可以作为JNLP运行,也可以作为独立的Jar运行,也可以作为已安装的Windows应用程序运行。在第一种和第二种情况下,我们希望应用程序尽可能自包含,因此我们将属性文件保存在应用程序启动的目录中。在后一种情况下(或者如果你在Linux或Mac上“安装”它),它会写入用户的主目录,使多个用户无需共享状态即可共享应用程序。

    public File getConfigFile(boolean isInstalled) {
      String filename = ".jGrep.conf"; // The . prefix means "hidden file" in Unix
      return isInstalled ? // indicates the application is "installed"
        new File(getUserDir(), filename) : // identifies the user's home directory
        new File(filename); // default to a file in the current directory
    }
    
  2. 在启动时/何时需要加载属性文件(如果存在):

    如果文件不存在,无法读取或包含意外内容,则此方法可能会引发多个异常。您将通过显式处理这些案例并为其提供有用的错误消息来为您的用户提供最佳服务,但是您可以只返回一个空属性文件; jGrep这样做,提供了一个无用的通用“无法打开”的消息 - 我应该去解决这个问题:)

    public Properties loadProperties(File f)
    throws FileNotFoundException, IOException, IllegalArgumentException {
        Properties prop = new Properties();
      try(FileInputStream in = new FileInputStream(f)) {
        prop.load(in); // Reads from the input stream
      }
      // catch (Exception e) { } // uncomment to suppress errors
      return prop;
    
  3. 退出/保存属性文件时,只要用户询问:

    现在我们以相同的方式保存我们的配置文件。在这里同样你可以抑制错误,但我特别不建议为这个方法做这个,因为用户希望保存他们的数据,并且应该清楚地告诉你错误是什么,如果你不能。

    public void saveProperties(Properties prop, File f)
    throws FileNotFoundException, IOException {
      try(FileOutputStream out = new FileOutputStream(f)) {
        prop.store(out, "AwesomeProgram configuration file");
        // return true; // uncomment if suppressing errors
      }
      // catch (Exception e) { return false; } // uncomment to suppress errors
    }
    
  4. 请注意,在这两种方法中,我们都没有表达编码 - 属性文件在Latin 1中用非转义字符的Java转义码(即\uXXXX)写出。如果您希望文件是人类可读的,则可以将Reader / Writer对象传递给属性文件,这可以是您想要的任何编码(UTF-8)。

    显然,如果您需要保存多个文件,只需将单独的文件保存/加载到不同的Properties对象即可。