Использование Preferences API
Существует немало случаев, когда программисту необходимо сохранять какие-нибудь конфигурационные параметры. Раньше это можно было сделать используя класс java.util.Properties. Поначалу это выглядело не так уж плохо, но тем не менее на программиста ложилась задача где разместить конфигурационный файл.
PreferencesAPI появился в Java 1.4 и по словам Sun призван обеспечить более удобный способ хранения и восстановления каких-либо настроек (для пользователя или системы). И теперь такого понятия как конфигурационный файл по сути нет. Насколько это хорошо, что нет конфигурационного файла – вопрос открыт. Но система позволяет вам не просто хранить данные, но и делать экспорт/импорт – вы получаете приятный XML, который можно переносить вместе с приложением.
И что еще более важно – теперь система позволяет делать не «плоский» конфигурационный файл, а иерархическую структуру.
Давайте немного рассмотрим как устроено сохрание данных изнутри. Данные настроек пользователя организуются в виде иерархической коллекции узлов – по сути это дерево. Существует два типа деревьев – для пользователя и для системы. Надо также отметить, что для каждого пользователя организуется своя иерархия данных пользователя. Для системы она одна для всех пользователей. Техника работы с обоими типами деревьев одинакова. Потму мы рассмотрим примеры для пользовательского дерева настроек.
Я запускал все примеры под Windows XP – для данной ОС конфигурация хранится в реестре. Вы можете запустить программу regedit и открыть ветку.
HKEY_CURRENT_USER\Software\JavaSoft\Prefs
Если ее там пока нет – не волнуйтесь, появится.
Для Linux создается каталог $HOME/.java/.userPrefs, внутри которого уже создается иерархия из директорий и конечного файла XML.
Давайте сразу рассмотрим несложный пример:
1 2 3 4 5 6 7 8 9 10 11 12 |
import java.util.prefs.Preferences; import java.awt.Dimension; public class UserPreferences { private Preferences userPrefs; public UserPreferences() { userPrefs = Preferences.userNodeForPackage(UserPreferences.class); } } |
Этот код позволяет нам получить экземпляр объекта Preferences который можно использовать для чтения или записи данных. В данном случае мы будем сохранять и восстанавливать размеры JFrame. Это показано в следующем примере, где мы рассмотрим все более подробно. В нем мы создаем форму с определенными значениями ширины и высоты (100, 200), после этого мы записываем новые значения (400, 500) и при повторном запуске мы получаем их из нашего хранилища.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import java.util.prefs.Preferences; import java.awt.Dimension; import javax.swing.JFrame; public class UserPreferences extends JFrame { private Preferences userPrefs; public UserPreferences() { // Здесь мы создаем объект для сохранения с именем “prefexample” userPrefs = Preferences.userRoot().node("prefexample"); setToPreferredSize(); // Форма при закрытии завершает приложение и делаем форму видимой setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public void setToPreferredSize() { int width = userPrefs.getInt("width", 100); int height = userPrefs.getInt("height", 200); setSize(width, height); } public Dimension getDimensions() { int width = userPrefs.getInt("width", 100); int height = userPrefs.getInt("height", 200); return new Dimension(width, height); } public void putDimensions(Dimension dimension) { userPrefs.putInt("width", (int)dimension.getWidth()); userPrefs.putInt("height", (int)dimension.getHeight()); } public static void main(String[] args) { UserPreferences up = new UserPreferences(); up.putDimensions(new Dimension(400,500)); } } |
Давайте внимательно рассмотрим основные моменты в программе.
Во-первых – конструктор. Здесь мы видим. что для начала работы необходимо получить обработчик Preferences для класса. Вызов
1 |
userPrefs = Preferences.userRoot().node("prefexample"); |
создает ветку HKEY_CURRENT_USER\Software\JavaSoft\Prefs\prefexample
Выше мы приводили еще один вариант создания ветки. В этом случае имя будет по умолчанию
HKEY_CURRENT_USER\Software\JavaSoft\Prefs\
Не знаю как создателям, а мне невозможность выбора имени не понравилась. Потому и нашел тот вызов который представлен выше.
Если посмотреть API для класса Preferences, то можно найти немало интересного – что я вам советую сделать после прочтения (если конечно есть время и желание). В принципе вся остальная информация основана именно на этом API.
Во-вторых – в том же конструкторе мы делаем вызов метода setToPreferredSize().
В этом методе мы пытаемся получить параметры по именам. Причем мы указываем не только имя, но и тип – getInt – т.е. просим именно целое число. Preferences включает методы для получения разных типов данных – целые, вещественные, логические, байтовые массивы. Вызов getInt также включает указание значения по умолчанию – если параметр не будет найден.
Таким образом при запуске мы сразу создаем форму и пытаемся в ее конструкторе получить данные из хранилища. Т.к. там еще ничего нет (при первом запуске) то форма получается в размерами 100 на 200. После этого мы делаем вызов putDimensions, который сохраняет новые значения ширины и высоты. Вызовы достаточно просты для понимания – там всего два параметра, имя и значение. Думаю, что после запуска надо посмотреть в реестр и увидеть. что там изменилось.
Еще раз вернемся к вызову
1 |
userPrefs = Preferences.userRoot().node("prefexample"); |
Что здесь очень инетересно, так это то. что вызов node возвращает объект Preferences, который опять же имеет вызов node.
Как легко можно увидеть, это дает нам возможность строить дерево конфигурации – иерархическую структуру. И класс Preferences предоставляет все необходимое для работы с деревом. Это и получение родителя для узла, и получение списка «детей» и другие методы.
Посмотрите результаты вызова
1 |
userPrefs = Preferences.userRoot().node("prefexample").node(“1”); |
На что я еще хотел бы обратить ваше внимание – Preferences API имеет листенер для отслеживания изменения параметров. Для этого надо реализовать интерфейс PreferenceChangeListener.
Давайте посмотрим несложный пример, который с определенным интервалом меняет высоту и ширину формы, но будет это делать не напрямую, а путем отслеживания изменений сохраненных параметров. Комментарии будут приведены прямо в тексте – я думаю, что это будет удобнее. Порядок просмотра советую выбрать такой – сначала посмотрите на конструктор (в общем-то он не очень сильно изменился). А потом сразу идите в метод resetDimensionsManyTimes. В нем мы в цикле через определенный интервал времени меняем значения параметров и сохраяем их в реестре. При сохранении вызывается обработчик preferenceChange, который считывает новые значение и устанавливает новые размеры формы. Также приведен пример экспорта данных в файл.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
import java.util.prefs.Preferences; import java.util.prefs.BackingStoreException; import java.util.prefs.PreferenceChangeListener; import java.util.prefs.PreferenceChangeEvent; import java.awt.Dimension; import javax.swing.JFrame; import java.io.FileOutputStream; public class UserPreferences extends JFrame implements PreferenceChangeListener { private Preferences userPrefs; public UserPreferences() { // Здесь мы создаем объект класса Preferences userPrefs = Preferences.userRoot().node("prefexample"); // Указываем слушателя для отслеживания изменений параметров userPrefs.addPreferenceChangeListener(this); // Устанавливаем первоначальные размеры setToPreferredSize(); // Форма при закрытии завершает приложение и делаем форму видимой setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public void setToPreferredSize() { // Получить значение параметров и установить размеры формы int width = userPrefs.getInt("width", 100); int height = userPrefs.getInt("height", 200); setSize(width, height); System.out.println("Width = "+ getWidth() + " Height = "+ getHeight()); } // Это наш обработчик событий при изменении параметров в реестре public void preferenceChange(PreferenceChangeEvent e) { setToPreferredSize(); } // Основная рабочая функция public void resetDimensionsManyTimes() { for (int i = 0; i<10; i++) { // Сохраняем случайные значения для высоты и ширины putRandomDimensions(); // И «засыпаем» на 5 секунд – можно быстро посмотреть в реестр, что там try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } // А это просто пример экспорта данных из реестра в xml. try { userPrefs.exportNode(new FileOutputStream("config.xml")); } catch(Exception e) { e.printStackTrace(); } } // Сохраняем параметры в виде случайных чисел private void putRandomDimensions() { userPrefs.putInt("width", getRandomInt()); userPrefs.putInt("height", getRandomInt()); } // Возвращаем случайное челое число от 100 до 400 private int getRandomInt() { return (int)(Math.random()*300+100); } // Данная функция нигде не вызывается. но если ее вызвать, // то все данные будут удалены из реестра public void clearPreferences() { try { userPrefs.clear(); } catch (BackingStoreException e) { e.printStackTrace(); } } public static void main(String[] args) { // Создаем форму UserPreferences up = new UserPreferences(); // Запускаем цикл изменений up.resetDimensionsManyTimes(); } } |
Вот и все, что я хотел бы вам рассказать про новое Preferences API. Все пожелания автор принимает с удовольствием.