Этот URL мы присвоим публичному члену класса CONTENT_URI_ITEMS, он будет часто использоваться и внутри нашей программы для доступа к данным. Другие приложения узнают о нем из системного манифеста.
Таким образом, программы могут взаимодействовать с хранилищами друг друга, используя URL. Как в Web, возвращаемый ответ снабжается указанием MIME-типа данных. Фактически программу Android можно рассматривать как Web-сервер в миниатюре, функционирующей в «карманном Интернете» – операционной системе Android. По аналогии с Интернетом, где требуется зарегистрировать домен, в Android необходимо зарегистрировать «адрес», чтобы публиковать данные.
Системный манифест – XML-файл, описывающий программу Android. Среда Eclipse создает файл манифеста AndroidManifest.xml автоматически. Но мы добавим к нему указание, что объект ItemsProvider по заданному URL предоставляет некие данные:
<provider class='ItemsProvider' authorities='ru.icsit.provider.Inventory' />
Из манифеста операционная система не только извлекает информацию о возможностях обмена данными, но и позволяет контролировать взаимодействие между программными компонентами. В SDK Android нет понятия «событие» для оповещения других приложений (как в Windows), но описанная выше техника вполне их заменяет. Получив запрос на предоставление данных, Android загрузит объект ItemsProvider, а тот обеспечит передачу нужной информации в соответствующем формате.

В нашем случае передается не текст, а специальная структура данных (курсор набора данных), значит, надо это как-то описать. В перекрываемом методе getType, в зависимости от переданного URL, вернем один из двух MIME-типов:
vnd.android.cursor.item/vnd.icsit.item
vnd.android.cursor.dir/vnd.icsit.item
(Можно передать и картинки, тогда нужно указать, например, тип image/jpeg.) Далее, если требуется отдельная запись, то ее можно запросить, указав идентификатор прямо в URL. Если идентификатора нет, вернем курсор для всего набора данных. Создадим объект URL_MATCHER класса ContentURIParser, задача которого – определить по URL, какие данные нам требуются. Добавим два варианта —.items и items/#.
Если URL завершается items/#, то вернем одну запись по ее идентификатору (вариант 2), иначе вернем весь набор данных (вариант 1). Перейдем к методу query. В параметре selection передается выражение SELECT за словом WHERE (мы используем SQLite, так что проблем нет. Если бы для хранения использовались файлы, этот параметр можно было бы просто игнорировать). Поскольку имеется СУБД, то можно управлять сортировкой и группировкой результатов (заблокировать такую возможность можно, если в вызове конструктора запроса не передавать эти параметры).
Перекрытые методы insert, update, delete проверяют правильность передаваемого URL и вызывают для выполнения операций вставки, обновления и удаления одноименные методы класса SQLiteDatabase, а те возвращают количество затронутых запросом строк. Приятная особенность, которую мы можем использовать в методе update: если в результате выполнения mDB.update()записей, удовлетворяющих условию, не обнаружилось (и ничего не обновилось), то вызывается метод вставки. (Строго говоря, не совсем корректное решение, зато упростит пример.)
Теперь перейдем к наполнению формы. Eclipse создал два файла для нашей единственной формы – inventory.java и main.xml и (уже в классе активности) добавил загрузку элементов управления по описанию в main.xml. Можно вызывать конструкторы элементов управления в коде, но это слишком трудоемко. Поэтому будем редактировать ресурс main.xml. На форму перетаскиваем элементы типа «поле ввода» для четырех полей в таблице, снабжаем их метками «Номер», «Описание», «Количество», «Размещение». Добавим также кнопки: со знаком «?» для поиска, «Схр» для сохранения изменений и одновременно для создания новой записи (для этого я и совместил insert с update в классе провайдера) и «-» для удаления записи. После ввода инвентарного номера и нажатия кнопки поиска все элементы управления заполнятся соответствующими значениями. Отредактировав их, можно сохранить изменения кнопкой «Схр».

В качестве визуального редактора формы лучше использовать утилиту DroidDraw. Можно отредактировать main.xml и вручную, но этого лучше не делать. Тут есть тонкость: текущая версия DroidDraw несовместима с кодировкой UTF-8, а поскольку на нашей форме используется кириллица, пришлось немного подправить файл руками. Заодно автоматически создаваемые идентификаторы элементов были переименованы в более осмысленные:
<Button id='@+id/bsearch' a: text='?' />
После сохранения формы ее требуется скомпилировать. Eclipse вызывает упаковщика ресурсов SDK aapt для генерации класса R, добавляемого в проект для указания идентификаторов ресурсов. Символы @+id/ означают инструкцию «поместить идентификатор bsearch в класс R». (Идентификаторы меток были удалены, поскольку нам не требуется ссылаться на них из программы.) Идентификаторы кнопок позволят назначить им обработчики нажатий, из полей ввода необходимо извлекать значения, так что им тоже нужны идентификаторы. Для отрисовки формы Android использует встроенный браузер. В целом принципы Android вряд ли станут чем-то новым для Web-разработчиков.

Готово. Соберем все вместе в коде класса активности inventory. Перекроем метод onCreate и убедимся, что не забываем загрузить форму main.xml из ресурсов:
setContentView(R.layout.main);
В ресурсы можно добавлять не только XML-файлы, но и картинки и мультимедиа-объекты (утилита aapt назначает им идентификаторы по названию файла). Инициализируем переменные, которые будут указывать на элементы ввода текста:
mEditNum = (TextView) findViewById(R.id.enumber);
Затем создадим (пока пустые) обработчиков нажатий
mButtonSearchClick, mButtonApplyClick, mButtonDeleteClick
для всех трех кнопок. Найдем загруженную кнопку по ее идентификатору, помещенному в классе R
findViewById(R.id.bsearch). setOnClickListener(mButtonSearchClick),
и назначим обработчика событий в методе onCreate.
Теперь остается написать код для этих трех событий. В обработчике кнопки сохранения введенные данные берутся из формы и помещаются в объект ContentValues, который затем передается по URL нужному провайдеру данных в метод update:
values.put(«invno», mEditNum.getText(). toString());
getContentResolver(). update(ItemsProvider.CONTENT_URI_ITEMS.addId(invno), values, null, null);
Как видно, URL указывает на объект класса ItemsProvider, а кроме того, содержит идентификатор