Глава 7. Поддержка постоянного хранения в MIDP

Реальные приложения создают данные, которые должны быть сохранены и использованы позже той же или другой программой. В этой главе вы узнаете, как использовать свойство постоянного хранения данных программного интерфейса приложения MIDP.

MIDP поддерживает постоянное хранение данных приложения с помощью системы управления записями (Record Management System (RMS)). Пакет javax.microedition.rms определяет программные интерфейсы приложения, поддерживающие постоянное хранение данных, которые содержит этот пакет.


Поддержка постоянного хранения устройством

Каждое соответствующее требованиям MIDP устройство поддерживает выделенную область памяти для постоянного хранения данных приложения. Данные MID-лета, хранящиеся там, постоянно существуют при множестве инициализаций приложения, которое их использует. Как физическое местоположение, так и размер хранилища данных зависят от устройства.

RMS API извлекает подробную информацию об области хранения устройства и доступе к этой информации, а также предоставляет единообразный механизм для создания, уничтожения и изменения данных. Это гарантирует переносимость MID-летов на различные устройства.


Модель хранения данных RMS

RMS поддерживает создание множества хранилищ записей, показанных на рисунке 7.1, и управление ими. Хранилище записей — это база данных, основным понятием которой является запись. Каждое хранилище записей содержит ноль или больше записей. Название хранилища записей чувствительно к регистру и может состоять максимум из 32 знаков уникода. Хранилище записей создается МШ-летом.



Рисунок 7.1. RMS состоит из одного или нескольких хранилищ записей, каждое из которых содержит ноль или более записей, представляющих собой массив байтов


MID-леты в пределах одного набора MID-летов могут совместно использовать хранилища записей друг друга. Набор MID-летов определяет пространство имен для хранилищ записей, хранилище записей должно иметь уникальное в пределах набора MID-летов имя. Однако в различных MID-летах могут использоваться одинаковые имена, MID-леты могут составлять список имен всех хранилищ записей, доступных им. Они также могут определять размер свободного места, доступного для хранения данных.

В этой связи вы должны знать, что когда все MID-леты в наборе MID-летов удаляются с устройства, AMS устройства удаляет все хранилища записей в пространстве имен набора MID-летов. Все данные постоянного хранения будут потеряны. По этой причине вы должны обдумать при разработке приложения включение предупреждения или подтверждения, требующего, чтобы пользователи подтвердили, что они поняли потенциальную угрозу потери данных при удалении приложений! Приложения могут также включать механизм резервного копирования записей хранилища данных в другое место. Это может потребовать поддержки со стороны сервера, задача, которую я описываю в главе 11.

RMS определяет следующие абстрактные операции для отдельного хранилища записей:

Добавление записи. Удаление записи. Изменение записи. Просмотр (извлечение) записи. Составление списка всех записей.

Записи однозначно идентифицируются с помощью ID записи, который является единственным поддерживаемым важнейшим ключевым типом. Тип ID всех записей является встроенным типом Java int. RMS не поддерживает свойств — таких, как таблицы, строки, столбцы, типы данных и так далее, — которые присутствуют в реляционных базах данных.


Записи

Запись является массивом байтов типа byte []. RMS не поддерживает описание или форматирование полей записи. Ваше приложение должно определять элементы данных записи и их формат.

Читатель записи поэтому должен знать формат, который использовался при ее создании. Поскольку запись является просто массивом байтов, приложения должны преобразовывать данные из произвольных типов в байты при создании записей, а затем преобразовывать их из байтов в типы при чтении данных.


Пример приложения

В остальной части этой главы описываются частные подробности RMS с помощью следующего примера, использующего базовые свойства RMS. Этот пример является простой адресной книгой, которая хранит имена и номера телефонов.

Многие из примеров имеют дело с созданием организации и структуры приложений MIDP. Большинство протекающих операций RMS ограничены одним классом. В этом примере вы можете видеть, как включать использование постоянного хранения в приложение, которое вы, вероятно, найдете на настоящем мобильном устройстве.

Конечно, вы можете создать и исполнить исходный код, приведенный в этой главе, для получения представления о том, как приложение продвигается вперед по различным экранам. Я оставляю это на ваше усмотрение вместо того, чтобы показывать вам здесь изображения всех этих экранов.

Следующие файлы включены в адресную книгу, описанную в данном примере:

— AddScreen.java;

— AddressBook.java;

— AddressBookMain.java;

— DeleteAllConfirmationScreen.java;

— PersistenceDemo.java;

— RecordList.java;

— SearchResultScreen.java;

— SearchScreen.java.


Подробные листинги этих файлов можно найти на Web-сайте «Prentice-Hall» по адресу http://www.phptr.com. Файл PersistenceDemo.java определяет MID-лет, который представляет меню, содержащее приложение адресной книги. Файл AddressBookMain.java определяет точку входа в приложение адресной книги.

В листинге 7.1 показан полный исходный код класса AddressBook.java. Этот класс извлекает подробную информацию о вызовах RMS API из остальной части МID-лета. При инициализации MID-лета он создает экземпляр класса AddressBook, который, в свою очередь, открывает хранилище записей с именем addressbook.


Листинг 7.1. Класс AddressBook позволяет приложению получать доступ к хранилищу записей


import javax.microedition.rms.RecordComparator;

import javax.microedition.rms.RecordEnumeration;

import javax.microedition.rms.RecordFilter;

import javax.microedition.rms.RecordStore;

import javax.microedition.rms.RecordStoreException;

import javax.microedition.rms.RecordStoreNotOpenException;

import Java.io.ByteArrayInputStream/

import java.io.ByteArrayOutputStream;

import Java.io.DatalnputStream;

import java.io.DataOutputStream;

import Java.io.lOException;

/**


Этот класс внедряет простую адресную книгу с целью демонстрации.

В нем хранятся записи, состоящие из полей имени String и номера телефона String.

Этот класс определяет два внутренних класса, один является блоком сравнения записей, а второй фильтром записей, используемым при извлечении записей.


*/

public class AddressBook

private static final String RECORD_STORE_NAME = «address-book»;

private RecordStore recordStore;

public AddressBook () throws RecordStoreException

super!);

recordStore = RecordStore.openRecordStore(RECORD_STORE_NAME, true);

{

void close() throws RecordStoreException

{

try

{

recordStore.closeRecordStore();

}

catch (RecordStoreNotOpenException rsno)

{

}

}

/*


Получает хранилище записей, используемое этим объектом.


@возвращаем ссылку на RecordStore, используемый этим объектом.

public RecordStore getRecordStore()

}

return recordStore;

/**


Добавляет указанную запись в хранилище записей данной адресной книги.


@param name имя входа было добавлено.

@parara phone телефонный номер для входа был добавлен.

@сбрасывает RecordStoreException, если есть проблемы с добавлением записи.

public void addRecord(String name, String phone)

throws RecordStoreException

}

ByteArrayOutputStreara baos = new ByteArrayOutputStream();

DataOutputStream dos = new DataOutputStream(baos);

try

dos.writeUTF(name); dos.writeUTF(phone);

}

catch (lOException ioe)

{

ioe.printStackTrace();

)

int id =

recordstore.addRecord(baos.toByteArray(), 0,

baos.toByteArrayO.lengthy-System, out. println ("Record id = " + id);

}

/**


RecordEnumerator, упорядочивающий записи в лексикографическом порядке по полям имен записей.


*/

RecordEnumeration getMatchesByNarae(String matchKey)

throws RecordStoreNotOpenException

(

MacchAllNaraesFilter filter =

new MatchAllNamesFilter(matchKey);

AlphabeticalOrdering comparator =

new AlphabeticalOrdering();

return recordStore.enuraerateRecords(filter,

comparator, false);

}

/**


RecordFilter, устанавливающий совпадение, если имя варианта (первое поле в записи варианта)!) точно соответствует имени элемента списка или 2) если строка имени элемента списка начинается с имени варианта. Возвращает значение true, ее установлено соответствие, false — в ином случае.


*/

class MatchAllNamesFilter implements RecordFilter

{

String requestString;

public MatchAllNamesFilter(String matchKey);

requestString = matchKey;

}

public boolean matches(byte [] candidate)

{

ByteArraylnputStream bais =

new ByteArraylnputStream(candidate);

DatalnputStream dis = new DatalnputStream(bais);

Siring name = null;

try

}

name = dis.readUTF();

if (name.indexOf(requestString) == 0)

return true;

else

return false;

}

catch (lOException ioe)

{

ioe.printStackTrace!);

return true;

}

}

/**


Этот внутренний класс реализует RecordCornparator, чья политика Заключается в выполнении сортировки по алфавиту.


*/

class AlphabeticalOrdering implements RecordCoraparator

}

Конструктор.

public AlphabeticalOrdering ()

(

)

public int compare(byte [] reel, byte [] rec2)

{

ByteArraylnputStream baisl =

new ByteArraylnputStream(recl);

DatalnputStream disl = new DatalnputStream(baisl);

ByteArraylnputStream bais2 =

new ByteArraylnputStream(rec2);

DatalnputStream dis2 = new DatalnputStream(bais2);

String namel = null; String name2 = null; try

namel = disl.readUTF ();

name2 = dis2.readUTF ();

}

catch (lOException ioe)

ioe.printStackTrace();

}

if (namel == null II name2 == null) return 0;

int result = namel.compareTo(name2);

if (result < 0)

return RecordCornparator. PRECEDES;

else if (result == 0)

return RecordCoraparator.EQUIVALENT;

else

return RecordComparator.FOLLOWS;

}

}

/**

Удаляет все записи из хранилища данных.

В текущих реализациях самый быстрый способ удаления всех записей заключается в удалении хранилища данных и повторном его создании, вместо удаления каждой записи по очереди!


void deleteAHRecords ()

}

try

RecordEnumeration re =

recordStore.enumerateRecords(null, null, false);

while (re.hasNextElement())

*/

int id = re.nextRecordld();

recordStore.deleteRecord(id);

}

}

catch (RecordStoreException rse)

{

rse.printStackTracel);

} }

/**


Получает статистику хранилища данных, используемого данной адресной книгой.

/**

возвращает String статистических данных.


*/

public String getStatistics ()

{

int numRecords = 0;

int space = 0;

StringBuffer stats = new StringBuffer("Records:

*/

try

{

numRecords = recordStore.getNumRecords ();

space = recordStore.getSizeAvailable();

)

catch (RecordStoreException rse)

(

rse.printStackTrace();

}

stats.append(String.valueOf(nuraRecords));

stats.append("\n\n");

stats.append("Available bytes: ");

stats.append(String.valueOf(space));

return stats. toString();

}

}


Обратите внимание, что класс AddressBook определяет член типа RecordStore. Это экземпляр действительного хранилища записей, используемого приложением. Класс RecordStore является единственным открыто объявляемым классом в пакете RMS. Он определяет абстракцию хранилища записей.

Конструктор AddressBook сбрасывает RecordStoreException, поскольку метод openRecordStore() может сбрасывать три исключения, которые происходят от него. Пакет javax.microedition.rras определяет пять исключений. На рисунке 7.2 показана иерархия наследования, которая содержит типы исключений RMS.



Рисунок 7.2. Пакет RMS определяет несколько исключений, связанных с доступом к хранилищу данных. Все исключения принадлежат пакету javax.microedition.rms, если не определено иное


Класс AddressBook предоставляет следующие методы, поддерживающие функции уровня приложения, выполняемые в хранилище данных:


void addRecord(String name, String phone)

void deleteAHReccrds ()

String ge-Scatistics ()

RecordEnumeration getAHRecords(String matchKey)


При реализации данного приложения на реальном устройстве необходимо обеспечение более полного набора методов для окончательной доработки этого интерфейса. Тем не менее, данный набор можно использовать с целью демонстрации понятий, связанных с использованием RMS MIDP.


Работа с данными byte []

Как уже упоминалось выше, приложение в этом примере работает с записями, которые содержат имя и номер телефона. Пользователь вводит как имена, так и телефонные номера как объекты String, поскольку экран ввода данных использует экземпляры класса TextField, описанный ранее в главе 5. Соответственно, метод addRecord () получает эти значения String и преобразует их в байты.

Так или иначе, эти значения должны быть преобразованы в один массив байтов перед добавлением в хранилище данных. Причина того, что вы должны выполнить это преобразование, заключается в том, что API RecordStore хранит записи только в виде однобайтового массива.

Метод addRecord () использует стандартную идиому ввода-вывода Java при создании DatalnputStream, который поддерживает запись встроенных типов Java в выходном потоке. Получающийся в результате байтовый массив затем добавляется в объект RecordStore.

Метод RecordStore.addRecord() возвращает int, которая представляет значение ID только что созданной записи. Ваше приложение может сохранить данный ID и использовать его при последующем извлечении записи. Но существует более интересный способ извлечения записей.


Cпиcки

Существует на самом деле два способа извлечения записей из хранилища данных:

— Извлечение отдельной записи с помощью ее уникального ID;

— Извлечение списка записей и выбор из них одной или нескольких нужных вам записей.

Чтобы извлечь определенную запись, вы можете использовать следующий метод класса RecordStore:

byte [] getRecord(int recordld)

Этот метод, очевидно, требует, чтобы вы знали уникальный ID записи, которую вы хотите извлечь. К сожалению, это означает, что вам, возможно, придется хранить ID где-нибудь в легкодоступном месте после того, как он будет выдан вам методом addRecord (). Это не всегда удобно или практично при большом количестве записей.

Самый легкий способ найти записи, которые вам нужны, — это использовать списки, которые поддерживаются классом RecordStore. Список весьма удобен при извлечении записей, если вы не знаете ID записей, которые вам нужны. Вы можете создать список записей, хранящихся в хранилище записей, а затем исследовать его, выбрав одну или несколько записей, которые вам нужны.


Класс RecordStore определяет метод


RecordEnumeration

enumerateRecords(RecordFilter filter,

RecordComparator comparator,

boolean keepUpdated)


который выдает список записей в хранилище записей. В листинге 7.2 показан исходный код RecordList.Java. Этот класс создает и отображает список всех записей адресной книги. Обратите внимание, что для того, чтобы извлекать записи, ID записей указывать не нужно.


Листинг 7.2. Списки дают вам возможность получать доступ к записям, не зная их идентификационных номеров (ID)


import javax.microedition.midlet.MIDlet;

import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.AlertType;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Display;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.List;

import javax.microedition.rms.RecordEnumeration;

import javax.microedition.rms.RecordStore;

import javax.microedition.rms.RecordStoreException;

import java.io.ByteArraylnputStream;

import Java.io.DatalnputStream; import Java.io.lOException;

/**


Этот класс является компонентом пользовательского интерфейса, отображающим список записей, находящихся в хранилище записей.

Он использует объект AddressBook, определенный классом MID-лета для данного приложения MID-лета.


@смотри AddressBook

@смотри AddressBookMain

*/

public class RecordList extends List

implements CommandListener

{

private static Command go =

new command("Go", Command.SCREEN, 1);

private static Command back =

new Command("Back", Command.BACK, 1);

private Display display;

private static RecordList instance;

/**


Конструктор.


@param title название экрана пользовательского интерфейса,

который является List.

*/

public RecordList (String title)

superltitle, List.IMPLICIT);

instance = this;

PersistenceDemo pDemo = PersistenceDemo.getlnstance ();

display = Display.get-Display (pDemo);

addCommand(back); setCommandListener (this);

if (buildRecordList() <= 0) setTitle("No records found");

}

/**


Возвращает один экземпляр данного класса.

Вызов этого метода перед созданием объекта возвращает нулевой указатель.


@возвращает экземпляр данного класса.

*/

public static RecordList getlnstance()

}

return instance;

}

void display ()

{

display.setCurrent (this);

{

/**


Создает список записей, хранящихся в хранилище записей. Выдает число найденных записей. Этот метод извлекает все записи из хранилища записей, то есть он не использует фильтров для извлечения записей. Он также не использует компараторов записей, так что не упорядочивает выводимые записи.

Этот метод не сбрасывает исключений, но находит исключения, которые происходят при доступе к хранилищу записей.

(@возвращает число записей, найденных в хранилище записей, или 0, если записей не найдено.


*/

int buildRecordList ()

{

AddressBook addressBook =

AddressBookMain.get Instance!). getAddressBook();

RecordStore recordStore = addressBook.getRecordStore();

int numRecords = 0; try

RecordEnuraeration re;

re = recordStore.enumerateRecords(null,

null, false);

if (re.numRecords() > 0)

{

ByteArraylnputStream bais = null;

DatalnputStreara dis = null;

String name = null;

while (re.hasNextElement())

byte [] record = re.nextRecord();

bais = new ByteArraylnputStream(record); dis = new DatalnputStrearn (bais);

String strRec = new String(record);

name = dis. readUTFO;

appendfname, null;

numRecords++;

)

)

else

}

Alert a = new Alert("No records",

"No records found in record store", null,

AlertType.CONFIRMATION); a.setTimeout(Alert.FOREVER);

display.setCurrent (a, AddressBookMain.get Instance ());

})

catch (RecordStoreException re)

re.printStackTrace(); Alert a = new Alert("Error retrieving record",

"Error retrieving record.", AlertType.CONFIRMATION);

a. setTimeout(Alert.FOREVER); display.setCurrent (a, this);

catch (lOException ioe)

}

ioe.printStackTrace();

}

finally

{

return numRecords;

{

public void coramandAction(Command c, Displayable d)

if (c == back)

AddressBookMain.getlnstancel). display ();

}

}

}


Метод buildRecordList() использует составление списка для получения всех записей, хранящихся в хранилище записей, а затем извлекает поле имени каждой из них, чтобы создать список всех имен. Вызов enumerateRecords () выдает RecordEnumeration, содержащий все записи. С помощью методов hasNextRecord() и nextRecord() цикл while просто извлекает имена из каждой записи и добавляет их в объект List для отображения.

Для каждой записи вы должны расшифровать байтовый массив обратно тому процессу, согласно которому вы создали запись ранее. Вы знаете, что первый элемент, имя, является string, так что вы можете преобразовать его из байтов в String. Обратите внимание, что та же самая идиома потока ввода-вывода Java используется здесь для создания DatalnputStream, который поддерживает API для легкого преобразования встроенных типов Java.


Фильтры записей

Следующий пример не осуществляет поиска определенных записей. Однако существует способ, при котором вы можете использовать списки для извлечения некоторого подмножества записей хранилища. Вы можете использовать списки для вывода записей, которые удовлетворяют некоторым критериям, которые вы указали.

Первый аргумент в методе enumerateRecords() указывает фильтр записей. Фильтр является объектом, определяющим семантику соответствия записи набору критериев, которые определяют, должна ли запись включаться в набор списка.

Фильтр записей является классом, реализующим интерфейс RecordFilter, который определяется в пакете javax.microedition.rms. Этот интерфейс определяет единственный метод boolean matches (byte [] candidate). Ваш подкласс RecordFilter задает этот метод и устанавливает критерии фильтрации записей, указанных в списке всех записей хранилища записей. Метод enumerateRecords() активизирует вашу реализацию на каждой записи, извлеченной из хранилища записей.

В листинге 7.3 показан код класса SearchScreen. Java. Он ищет записи, которые начинаются с подстроки, введенной пользователем, или эквивалентные указанной пользователем строке.


Листинг 7.3. Поиск имен, которые начинаются с подстроки, введенной пользователем, использует API в классе AddressBook, определяющем семантику поиска


import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Display;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.TextField;

import javax.microedition.rms.RecordEnumeration;

import javax.microedition.rms.RecordStoreException;

import Java.util.Enumeration;

import Java.util.Vector;

/**


Этот класс внедряет экран, который дает возможность пользователю искать одну или несколько определенных записей в адресной книге. Пользователь вводит имя или префикс, который представляет имя одной или нескольких записей в адресной книге.


*/

public class SearchScreen extends Form

implements CommandListener

{

private static Command go =

new Command("Go", Command.SCREEN, 1);

private static Command back = new Command("Back", Command.BACK, 1);

private static SearchScreen instance; private Display display;

private AddressBookMain addressBook; private TextField keyEntry;

/**


Конструктор.


*/

public SearchScreen(}

(

super("Search for entry");

instance = this;

PersistenceDerao pDemo = PersistenceDemo.getlnstance ();

display = Display.getDisplay (pDerno);

addressBook = AddressBookMain.getlnstance ();

keyEntry = new TextField("Enter name",

null, 20, TextFieid.ANY); append(keyEntry);

addCommand(go); addCommand(back);

setCoramandListener(this);

}

/**


Возвращает один экземпляр данного класса.

Вызов данного метода до создания объекта возвращает нулевой указатель.

/**

возвращает экземпляр данного класса.


**/

public static SearchScreen getlnstance ()

return instance;) void display!)

(display.setCurrentlthis);

}

/**


Отображает данные, переданные на экран.

На самом деле этот метод передает обязанности по отображению данных экземпляру SearchResultScreen. Этот метод, однако, устанавливает новый экземпляр данного класса на текущее отображение.

Затрата выражается в Vector записей из хранилища записей адресной книги.


*/

void displaySearchResults(Vector results)

SearchResultScreen screen =

new SearchResultScreen (results);

display. setCurrenJ: (screen);

)


Создает конечный набор записей, соответствующих указанному имени.

Критерии отбора заключаются в том, что запись должна соответствовать имени, введенному пользователем в TextField «keyEntry». Этот метод задействует метод AddressBook.getMatchesByName() для применения специального фильтра, определяющего соответствие этого имени.


*/

Vector buildSearchResults()

{

AddressBook addressBook =

AddressBookMain.getInstance(). getAddressBook();

String matchKey = keyEntry.getString(); Vector results = new Vector();

try

{

RecordEnuraeration re =

addressBook.getMatchesByName(matchKey);

byte [] record = null;

while (re.hasNextElement())

record = re.nextRecord (); results.addElement(record);

}

}

catch (RecordStoreException rse)

}

rse.printStackTracet);

)

return results;

)

/**


Создает результаты поиска и отображает их на экране.


class BuildSearchResultsAction implements Runnable

{

public void run ()

Vector results = buildSearchResults ();

displaySearchResults(results);

}

}

public void commandAction(Command c, Displayable d);

if (c == go)

Runnable action = new BuildSearchResultsAction();

action.run ();

)

else if (c == beck)

}

AddressBookMain.getInstanced.display!);

}

}

}


Метод buildSearchResults() в классе SearchScreen получает список записей, вызывая метод getMatchesByName (String matchKey) в классе AddressBook. Этот метод фильтрует записи для вывода лишь тех, в которых поле имени начинается с matchKey.

Метод getMatchesByName () выполняет эту фильтрацию, пересылая фильтр записей как первый аргумент в метод enumerateRecords (). Экземпляр MatchAllNamesFilter определяет семантику фильтра для нахождения всех записей, которые начинаются с подстроки matchKey.

Метод enumerateRecords () обращается к следующему методу объекта фильтра для каждой записи в хранилище:

boolean matches(byte [] candidate)

Если в результате выводится true, он включает эту запись в набор списка. Теоретически это сходно с определением запроса SQL в системе родственных баз данных. Объект RecordFilter определяет критерии поиска.

Обратите внимание, что в листинге 7.2 аргумент RecordFilter был равен нулю. Таким образом класс RecordList может вывести все записи в списке, фильтр не применяется.

Вы можете описать несколько фильтров для поддержки поиска по различным критериям. Следуя программе листинга 7.4, вы можете определить несколько внутренних классов, которые реализуют RecordFilter и используют внутренний класс, соответствующий осуществляемому поиску.


Компараторы записей

Вы, несомненно, заметили, что второй аргумент, пересланный в enumerateRecords () в предыдущих примерах, был равен нулю. Этот второй параметр является «заполнителем» для компаратора записей. Компаратор записей — это объект, который сравнивает две записи для определения их упорядочивания или сортировки. Компараторы предоставляют приложениям возможность выполнять различную сортировку.

Как и фильтры, компараторы определяют семантику функции сравнения. Компаратор записей является реализацией интерфейса RecordComparator, который определяет единственный метод

int ccmparefbyte [] recordl, byte [] record2)

Компаратор также определяет три константы, описанные в таблице 7.1, которые ваша реализация должна использовать как текущие выводимые значения данного метода.


Таблица 7.1. Константы RecordComparator


Константа — Описание

public static int EQUIVALENT — Две записи эквивалентны в соответствии с семантикой сравнения

public static int FOLLOWS — Запись 1 «больше», чем запись 2, в соответствии с семантикой сравнения

public static int PRECEDES — Запись 1 «меньше», чем запись 2, в соответствии с семантикой сравнения


Идея использования компараторов сходна с понятием фильтрации записей. Вы определяете класс, который реализует интерфейс javax.microedition.rras.RecordComparator. Вы передаете его экземпляр в вызов enumerateRecords (). Записи, извлеченные из хранилища записей, сравниваются друг с другом, по две одновременно, а затем сортируются в соответствии с результатами сравнения. Вы можете таким образом извлекать записи из списка в порядке, определяемом компаратором.

В листинге 7.4 демонстрируется использование компаратора записей. Он определяет новый внутренний класс класса AddressBook, который вы видели в листинге 7.1. Новый внутренний класс AlphabeticalOrdering реализует RecordComparator. Его метод сравнения извлекает поле имени из каждого параметра байтового массива и сравнивает их лексикографически (по словам).


Листинг 7.4. Этот компаратор записей определяет семантику упорядочивания записей, базируясь на лексикографической сортировке значений их полей имени


/* *

Этот внутренний класс реализует RecordComparator, чья политика заключается в выполнении сортировки по алфавиту.


*/

class AlphabeticalOrdering implements RecordComparator

/**


Конструктор No-arg.


*/

public AlphabeticalOrdering()

}

super();)

public int comparelbyte [] reel, byte [] rec2)

ByteArraylnputStream baisl =

new ByteArraylnputStream(reel);

DatalnputStream disl = new DatalnputStream (baisl);

ByteArraylnputStream bais2 —

new ByteArraylnputStream(rec2);

DatalnputStream dis2 = new DatalnputStream(bais2);

String namel = null;

String name2 = null; try

(

namel = disl.readUTF ();

name2 = dis2.readUTF ();

catch (lOExceotion ioe)

ioe.pnntStackTrace ();

}

if (namel == null I | name2 == null) return 0;

int result = namel.compareTo(narae2);

if (result < 0)

return RecordComparater.PRECEDES;

else if (result == 0)

return RecordComparator.EQUIVALENT;

else

return RecordComparator.FOLLOWS;

}

}


Ваша адресная книга может использовать этот новый компаратор для лексикографической сортировки списка имен, извлеченных из хранилища записей. Например, чтобы отсортировать имена, выведенные поиском, вы просто создаете экземпляр вашего нового компаратора и пересылаете его как второй аргумент в вызов enumerateRecords (). Следующий фрагмент кода, показанный в листинге 7.5, является новой версией вызова метода getMatchesByName(String matchKey) в классе AddressBook.


Листинг 7.5. Чтобы осуществить сортировку, просто перешлите экземпляр компаратора в вызов списка записей из хранилища записей. Различные списки могут определять различную политику сортировки


RecordEnumeration getMatchesByName(String matchKey)

throws RecordStoreNotOpenException

{

MatchAllNaraesFilter filter =

new MatchAHNamesFilter (matchKey);

AlphabeticalOrdering comparator =

new AlphabeticalOrdering();

return recordStore.enumerateRecords(filter,

comparator, false);

}


Вы можете запустить это приложение и определить для себя, какие из записей, выведенных в результате поиска, теперь будут отсортированы лексикографически. Вы также можете использовать этот компаратор для сортировки имен, выводимых в List функцией ввода адресной книги. Вместо пересылки null как для фильтра, так и для компаратора перешлите экземпляр компаратора AlphabeticalOrdering при извлечении списка всех записей.


Блоки прослушивания записей

Приложения имеют способность получать уведомления при добавлении записи, ее удалении или изменении в хранилище записей. Класс RecordStore позволяет вам добавлять и удалять блоки прослушивания записей из определенного хранилища данных с помощью методов, перечисленных в таблице 7.2. Блок прослушивания записей является любым классом, реализующим интерфейс RecordListener, определенный в пакете javax.microedition.rms. Он объявляет три метода, показанных в таблице 7.3.


Таблица 7.2. Методы поддержки блока прослушивания событий RecordStore


Название метода RecordStore — Описание

Void addRecordListener (RecordListener listener) — Делает указанный объект блоком прослушивания для данного хранилища записей

Void removeRecordListener (RecordListener listener) — Удаляет указанный блок прослушивания как блок прослушивания данного хранилища записей


Таблица 7.3. Методы интерфейса RecordListener


Название метода RecordListener — Описание

void recordAdded (RecordStore recordStore, int recordld) — Уведомляет блок прослушивания записей о том, что запись была добавлена в указанное хранилище записей с указанным ID

void recordChanged (RecordStore recordStore, int recordld) — Уведомляет блок прослушивания записей о том, что запись с указанным ID была изменена в хранилище записей

void recordDeleted (RecordStore recordStore, int recordld) — Уведомляет блок прослушивания записей о том, что запись с указанным ID была удалена из хранилища записей


Возможность связывать блоки прослушивания с хранилищами записей означает, что ваши блоки прослушивания могут быть уведомлены об изменении любой записи в хранилище записей, к которому данные блоки прослушивания относятся. Необходимо переслать обратно информацию о задействованном хранилище записей, потому что ваш блок прослушивания может без труда регистрироваться более чем с одним хранилищем записей. Идея регистрации блока прослушивания записей сходна с идиомой, используемой любым другим блоком прослушивания событий, так что я не буду описывать здесь примеры кодов.


Различные свойства хранилищ записей

Класс RecordStore определяет несколько других свойств, которые полезны для приложений. В таблице 7.4 перечислены некоторые из других методов класса RecordStore и кратко описано их использование.


Таблица 7.4. Методы класса RecordStore


Название метода — Описание

void closeRecordStore () — Закрывает хранилище записей

static void deleteRecordStore () — Удаляет хранилище записей

long getLastModified () — Выдает время последней модификации

String getName () — Выдает название хранилища записей

int getNumRecords () — Выдает число записей в хранилище

byte [] getRecordfint recordl () — Извлекает запись по Ю

byte [] getRecord(int recordld, byte [] buffer, int offset) — Получает запись и помещает ее в предоставленный буфер

byte [] getRecordSize (int recordld) — Получает размер указанной записи

int getSize () — Выдает размер места (в байтах), которое занимает хранилище записей

int getSizeAvailable () — Выдает число оставшихся байтов, на которое хранилище записей может вырасти

int getVersion() — Выдает номер версии хранилища записей

static String [] listRecordStores () — Выдает список всех хранилищ записей, доступных набору MID-летов

static RecordStore openRecordStore (String name, boolean createlfNecessary) — Открывает указанное хранилище записей, создавая его, если оно не существует


Выводы по главе

Система управления записями (RMS) MIDP поддерживает постоянное хранение записей данных в зависимости от устройства. Класс RecordStore предоставляет API для постоянного хранения данных и извлекает подробную информацию о доступе к определяемым устройством областям хранения.

Хранилища записей определяются по именам, которые состоят максимум из 32 знаков уникода. Хранилища записей могут совместно использоваться MID-летами, находящимися в одном наборе MID-летов.

RMS определяет простую абстракцию базы данных, связанную с записями. Записи хранятся как массив байтов. Хранилище записей не имеет понятий встроенных типов Java.

Вы можете извлекать записи, предоставляя уникальный ID записи. Либо вы можете извлекать записи, получая список записей из RecordStore.

Списки необходимы для поиска записей в хранилище записей. Теоретически фильтры записей предоставляют своего рода механизм запросов. В связи с возможностью составления списков в RecordStore, фильтры записей поддерживают поиск только тех записей, которые соответствуют одному или нескольким критериям. Фильтр записей, класс, который реализует интерфейс RecordFilter, определяет критерии поиска.

Компараторы записей предоставляют возможность сортировки записей, извлекаемых из списка. Компараторы определяют политику сортировки и используются с механизмом составления списка. Реализация RecordComparator определяет семантику сортировки.

Блоки прослушивания записей являются блоками прослушивания, регистрирующимися с определенным хранилищем записей. Они дают возможность уведомления вашей программы об изменениях, вносимых в любую запись, находящуюся в хранилище записей.

Производительность является важной проблемой при доступе к хранилищу записей. Производительность современных реализаций RMS довольно низка. Разработчики приложений должны с осторожностью подходить к использованию RMS, применяя ее только тогда, когда это необходимо. Они должны рассматривать другие альтернативы постоянного хранения данных и сравнивать различные варианты.

Разработчики должны также измерять производительность их реализации RMS при запуске приложений, чтобы убедиться, что производительность приемлема для конечных пользователей. Бывало, что действующие приложения начинали работать слишком медленно из-за использования обновлений хранилища записей. Подтверждено, что перезапись приложений таким образом, чтобы все содержимое хранилища записей было загружено и перемещено, быстрее, чем выполнение обновлений в измененных элементах!







 

Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх