Android что такое contentvalues

Кошкин дом. Контент-провайдер. Часть третья

Во второй части мы использовали SQLite. Переделаем пример с использованием контент-провайдера.

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

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

Контент-провайдер можно рассматривать как посредник между компонентами приложения, которые вносят данные и самой базой данных. Контент-провайдер берёт на себя обработку данных, следя за правильностью данных. Кроме того, данные могут содержаться не только в базе данных SQLite, но и в других типах базы данных, в файлах и в других источниках. В любом случае приложение не должно переписываться при изменении источника данных. Провайдер берёт всё на себя.

При работе с контент-провайдером вы увидите, что многие методы дублируются. Так же как и с SQLite вам нужно реализовать методы query(), insert(), update(), delete().

Создание контент-провайдера

В пакете data создаём новый класс GuestProvider на основе ContentProvider. Студия предложит добавить обязательные методы.

Далее пишется очень много кода.

Главное — у провайдера есть основые методы: onCreate(), query(), insert(), update(), delete(), getType(). Часть есть методов выполняют ту же задачу, что и одноимённые методы в SQLiteDatabase.

В методе query() формируется запрос в зависимости от того, что мы хотим получить — информацию о всей таблице или только об одном ряде через switch. Первый аргумент метода uri поможет узнать наше намерение. Метод sUriMatcher.match(uri) вернёт нам нужную константу для дальнейшей работы. Остальные аргументы нам уже знакомы.

Также надо добавить информацию в манифест.

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

Адрес всегда должен начинаться с content://, очень похоже на http: и указывает на схему.

Далее идёт адрес для Content Authority, который обычно совпадает с пакетом вашего приложения. Он прописывается в манифесте (см. выше). Добавляем константу в класс HotelContract (см. ниже код).

Соединяем строку content:// с созданной константой в виде отдельной константы. Для работы с полученной строкой в виде Uri применим метод parse(), который как раз и возвратит объект Uri (см. ниже код).

Третья часть после слеша — это сами данные. В большинстве случаев это таблица вашей базы данных. Добавим ещё одну константу, которая будет присоединяться к предыдущему Uri (см. ниже код).

В классе GuestEntry добавляем дополнительную константу, которая соединяет все три части (см. ниже код).

После имени таблицы может идти четвёртая часть — один ряд из таблицы, который нам нужен для получения детальной информации, например, о госте. Для доступа к ряду таблицы можно использовать его идентификатор _id.

Таким образом адрес контент-провайдера нашего приложения: content://ru.alexanderklimov.cathouse/guests.

Информация о втором госте: content://ru.alexanderklimov.cathouse/guests/2

Два указанных случая — это адреса Uri. Если нам нужно получить список всех гостей таблицы, то мы используем первый Uri без использования идентификатора. Соответственно для отдельного гостя используем второй Uri.

Внесём изменения в класс HotelContract.

Вернёмся в класс MainActivity и перепишем метод displayDatabaseInfo().

Создавать и открывать базу данных теперь нет необходимости, это сделает контент-провайдер. Закомментируем строчу.

Запрос db.query() можно заменить на аналогичный от контент-провайдера, используя метод getContentResolver().

Метод query() отличается только первым параметром, вместо имени таблицы мы используем адрес таблицы.

Вернёмся в класс контент-провайдера. Метод insert() использует два параметра. Первый — URI также предназначен для выбора задачи, но вставка новой записи нужна только для всей таблицы, поэтому оператор switch будет состоять из одного варианта с константой GUESTS, вариант GUEST_ID не понадобится.

В классе MainActivity мы можем теперь отредактировать метод вставки новой записи в таблицу через контент-провайдер, убрав вызов базы данных и добавив и заменив вызов метода SQLiteDatabase.insert() на getContentResolver().insert().

Аналогично поправим метод в EditorActivity:

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

Перейдём к следующему этапу. Заменим TextView на ListView.

Создадим разметку res/layout/list_item.xml для отдельного элемента списка, состоящий из двух текстовых меток для имени и города.

В макете content_main.xml заменим TextView на ListView. Я оставил на память, позже можно удалить TextView. Также добавлены компоненты на случай, если список будет пуст.

Вот так будет выглядеть сейчас экран активности.

Добавим адаптер, который будет связывать данные из провайдера со списком.

В MainActivity добавим ссылки на новые компоненты.

Извлечение данных является трудоёмким процессом, поэтому следует использовать отдельный поток для этой операции. Для работы с курсорами существует интерфейс LoaderManager.LoaderCallbacks с тремя методами. Реализуем его в активности.

Удалим метод displayDatabaseInfo() и метод активности onStart(). Теперь данные мы будем получать через загрузчик. Добавим код в onCreate().

Также можно удалить и TextView. Все данные теперь будут попадать в список.

Переходим в GuestProvider и в методе query() добавляем строчку кода перед return:

Похожий код вставляем в метод insertGuest():

Читайте также:  Внешняя клавиатура для планшета андроид

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

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

В активности EditorActivity добавим код для приёма намерения.

Теперь мы можем переходить по щелчку по элементу списка. При этом заголовок будет «Изменение данных», чтобы было понятно, что мы находимся в режиме редактирования данных о госте.

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

Инициализируем загрузчик в onCreate().

Заменим метод insertGuest() на расширенную версию метода saveGuest(), который будет вставлять или редактировать данные в зависимости от логики программы.

Также включим «защиту от дурака», если попытаемся завести пустые данные.

Осталось написать код для удаления гостя из базы данных.

На главной активность есть пункт меню «Удалить всех гостей».

Код получился просто громадным. Если не заниматься контент-провайдерами постоянно, то вряд ли получится самостоятельно написать такую программу. Вот почему разработчики не любят контент-провайдер. Также вы можете пройти курс на Udacity — Android Basics: Data Storage | Udacity, в котором рассматривается контент-провайдер очень подробно (на англ.). В целях совместимости я даже переписал код, максимально похожий на код из курса.

Источник

SQLite на Android

SQLite доступен на любом Android-устройстве, его не нужно устанавливать отдельно.

SQLite поддерживает типы TEXT (аналог String в Java), INTEGER (аналог long в Java) и REAL (аналог double в Java). Остальные типы следует конвертировать, прежде чем сохранять в базе данных. SQLite сама по себе не проверяет типы данных, поэтому вы можете записать целое число в колонку, предназначенную для строк, и наоборот.

Тип Описание
NULL пустое значение
INTEGER целочисленное значение
REAL значение с плавающей точкой
TEXT строки или символы в кодировке UTF-8, UTF-16BE или UTF-16LE
NUMERIC здесь можно хранить булевы значения, а также время и дату
BLOB бинарные данные

Вы не найдёте здесь тип, работающий с датами. Можно использовать строковые значения, например, как 2013-03-28 (28 марта 2013 года). Для даты со временем рекомендуется использовать формат 2013-03-27T07:58. В таких случаях можно использовать некоторые функции SQLite для добавления дней, установки начала месяца и т.д. Учтите, что SQLite не поддерживает часовые пояса.

Также не поддерживается тип boolean. Используйте числа 0 для false и 1 для true.

Не используйте тип BLOB для хранения данных (картинки) в Android. Лучше хранить в базе путь к изображениям, а сами изображения хранить в файловой системе.

Обратите внимание, что здесь не используется популярный в MySQL тип varchar(n), ограничивающий длину строки.

Всё, что вам нужно для начала работы с базой данных — задать необходимые настройки для создания или обновления базы данных.

Так как сама база данных SQLite представляет собой файл, то по сути при работе с базой данных, вы взаимодействуете с файлом. Поэтому операции чтения и записи могут быть довольно медленными. Настоятельно рекомендуется использовать асинхронные операции, например, при помощи класса AsyncTask.

Когда ваше приложение создаёт базу данных, она сохраняется в каталоге DATA/data/имя_пакета/databases/имя_базы.db.

Метод Environment.getDataDirectory() возвращает путь к каталогу DATA.

Основными пакетами для работы с базой данных являются android.database и android.database.sqlite.

База данных SQLite доступна только приложению, которое создаёт её. Если вы хотите дать доступ к данным другим приложениям, вы можете использовать контент-провайдеры (ContentProvider).

Запускаем SQLite на эмуляторе

С помощью утилиты ADB можно запустить SQLite на эмуляторе и работать с базами данных напрямую.

Напомню, что можно активировать команду sqlitе3 для одной из перечисленных баз данных, введя следующую команду:

Для завершения работы с sqlite3 напишите:

Обратите внимание: приглашение для adb — это #, а приглашение для sqlitе3 — это sqlite>.

Описание доступных команд sqlite3 есть на сайте: http://www.sqlite.org/sqlite.html. Перечислим некоторые важные команды.

Чтобы просмотреть список таблиц:

Быстрый доступ к главной таблице:

Таблица sqlite_master — это главная таблица (master table), в которой отслеживаются таблицы и виды, содержащиеся в базе данных. Следующая команда распечатывает инструкцию create для таблицы people, находящейся в базе данных contacts.db:

Это один из способов, позволяющих узнать названия всех столбцов, которые содержатся в таблице базы данных. Можно скопировать базу данных на локальный компьютер и изучать её в более комфортных условиях. Чтобы переместить файл contacts.db, можно дать следующую команду:

Классы для работы с SQLite

Работа с базой данных сводится к следующим задачам:

  • Создание и открытие базы данных
  • Создание таблицы
  • Создание интерфейса для вставки данных (insert)
  • Создание интерфейса для выполнения запросов (выборка данных)
  • Закрытие базы данных

Класс ContentValues

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

Курсоры

В Android запросы к базе данных возвращают объекты класса Cursor. Вместо того чтобы извлекать данные и возвращать копию значений, курсоры ссылаются на результирующий набор исходных данных. Курсоры позволяют управлять текущей позицией (строкой) в результирующем наборе данных, возвращаемом при запросе.

Класс SQLiteOpenHelper: Создание базы данных

Библиотека Android содержит абстрактный класс SQLiteOpenHelper, с помощью которого можно создавать, открывать и обновлять базы данных. Это основной класс, с которым вам придётся работать в своих проектах. При реализации этого вспомогательного класса от вас скрывается логика, на основе которой принимается решение о создании или обновлении базы данных перед ее открытием. Класс SQLiteOpenHelper содержит два обязательных абстрактных метода:

  • onCreate() — вызывается при первом создании базы данных
  • onUpgrade() — вызывается при модификации базы данных
Читайте также:  Как отключить подписку эпл с андроида

Также используются другие методы класса:

  • onDowngrade(SQLiteDatabase, int, int)
  • onOpen(SQLiteDatabase)
  • getReadableDatabase()
  • getWritableDatabase()

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

В этом же классе принято объявлять открытые строковые константы для названия таблиц и полей создаваемой базы данных, которые клиенты могут использовать для определения столбцов при выполнении запросов к базе данных. Например, так можно объявить константы для таблицы CONTACT:

Лучше, если вы будете давать сразу понятные имена, указывающие на работу с таблицей. Если имя переменной TABLE_NAME ещё можно оставить, то для других лучше использовать более говорящие имена, например, KEY_NAME и KEY_PHONE или COLUMN_NAME, COLUMN_PHONE.

Ваш класс, расширяющий SQLiteOpenHelper, также неявно наследует интерфейс BaseColumns, в котором определена строковая константа _ID, представляющая имя поля для идентификаторов записей. В создаваемых таблицах базы данных поле _ID должно иметь тип INTEGER PRIMARY KEY AUTOINCREMENT. Описатель AUTOINCREMENT является необязательным. Часто в других примерах идентификатор создаётся вручную, смотрите как вам удобнее. Только всегда называйте его именно _id. Такое название используется в Android для работы с курсорами и поэтому придерживайтесь данного правила.

В методе onCreate() необходимо реализовать логику создания таблиц и при необходимости заполнить их начальными данными при помощи SQL-команды, например:

Здесь показана условная команда, в реальном коде вам нужно учитывать пробелы между константами, чтобы получилась правильная строка SQL-команды.

Также надо указать номер версии базы данных, начиная со значения 1. По этому номеру класс-обёртка будет понимать, что структуру базы данных следует обновить.

В методе onUpgrade() можно, например, реализовать запрос в базу данных на уничтожение таблицы (DROP TABLE), после чего вновь вызвать метод onCreate() для создания версии таблицы с обновленной структурой, например, так:

В параметрах метода используются номера версий базы данных — старая и новая. О номере версии говорилось выше.

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

При первой установке приложения базы данных ещё не существует. Тут проверять пока нечего. При установке новой версии приложения система проверит номер версии базы данных. Если он больше, чем было, то вызовется метод onUpgrade(). Если наоборот, то вызовется метод onDowngrade() (обычно так происходит редко).

Как использовать? Просто ставим условие на проверку.

Допустим, в таблицу нужно добавить новую колонку в дополнение к существующим колонкам NAME, DESCRIPTION и IMAGE_RESOURCE_ID. Для изменения таблицы используется выражение SQL с ключевым словом ALTER

Для замены имени таблицы используется выражение (меняем имя таблицы DOG на CAT):

Для удаления таблицы:

Соответственно, нужно вызвать метод SQLiteDatabase.execSQL(), передав ему нужную команду SQL.

Для удобства создадим вспомогательный метод updateDatabase().

Созданный метод теперь можно использовать в коде следующим образом.

Чтобы использовать реализацию вспомогательного класса, создайте новый экземпляр, передайте его конструктору контекст, имя базы данных, текущую версию и объект класса CursorFactory (если вы его используете). Вызовите метод getReadableDatabase() или getWritableDatabase(), чтобы открыть и вернуть экземпляр базы данных, с которой мы имеем дело (он будет доступен как для чтения, так и для записи). Вызов метода getWritableDatabase() может завершиться неудачно из-за проблем с полномочиями или нехваткой места на диске, поэтому лучше предусмотреть откат к методу getReadableDatabase().

Если база данных не существует, вспомогательный класс вызывает свой обработчик onCreate(); если версия базы данных изменилась, вызовется обработчик onUpgrade(). В любом случае вызов методов getWritableDatabase/getReadableDatabase, в зависимости от ситуации, вернет существующую, только что созданную или обновленную базу данных.

Оба метода имеют разные названия, но возвращают один и тот же объект. Только метод для чтения getReadableDatabase() сначала проверит доступность на чтение/запись. В случае ошибки метод проверит доступность на чтение и только потом уже вызовет исключение. Второй метод сразу проверяет доступ к чтению/записи и вызывает исключение при отказе в доступе.

Если вы планируете использовать несколько таблиц, то рекомендуется создавать для каждой таблицы отдельный класс.

SQLiteDatabase

Для управления базой данных SQLite существует класс SQLiteDatabase. В классе SQLiteDatabase определены методы query(), insert(), delete() и update() для чтения, добавления, удаления, изменения данных. Кроме того, метод execSQL() позволяет выполнять любой допустимый код на языке SQL применимо к таблицам базы данных, если вы хотите провести эти (или любые другие) операции вручную.

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

Для составления запроса используются два метода: rawQuery() и query(), а также класс SQLiteQueryBuilder.

Метод query()

Для чтения данных используют вызов метода query():

В метод query() передают семь параметров. Если какой-то параметр для запроса вас не интересует, то оставляете null:

  • table — имя таблицы, к которой передается запрос;
  • String[] columnNames — список имен возвращаемых полей (массив). При передаче null возвращаются все столбцы;
  • String whereClause — параметр, формирующий выражение WHERE (исключая сам оператор WHERE). Значение null возвращает все строки. Например: _id = 19 and summary = ?
  • String[] selectionArgs — значения аргументов фильтра. Вы можете включить ? в «whereClause»». Подставляется в запрос из заданного массива;
  • String[] groupBy — фильтр для группировки, формирующий выражение GROUP BY (исключая сам оператор GROUP BY). Если GROUP BY не нужен, передается null;
  • String[] having — фильтр для группировки, формирующий выражение HAVING (исключая сам оператор HAVING). Если не нужен, передается null;
  • String[] orderBy — параметр, формирующий выражение ORDER BY (исключая сам оператор ORDER BY). При сортировке по умолчанию передается null.
Читайте также:  Как сбросить android zte до заводских настроек

Объект Cursor, возвращаемый методом query(), обеспечивает доступ к набору записей результирующей выборки. Для обработки возвращаемых данных объект Cursor имеет набор методов для чтения каждого типа данных — getString(), getInt() и getFloat().

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

Для запроса с условием используются третий и четвёртый параметр. Например, нам нужны записи, у которого имя кота будет Барсик:

Параметр «NAME = ?» означает, что столбец NAME должен содержать значение, указанное следующем параметре.

Множественные условия. Об этом говорилось выше.

Пример при использовании числовых значений, например, идентификатор первичного ключа (об этом тоже говорилось)

Для сортировки используется последний параметр. Нужно указать нужный столбец, по которому будет проводиться сортировка. По алфавиту с буквы A — ASC, наоборот — DESC

Возможна сортировка по нескольким столбцам.

Вы можете использовать функции SQL для составления запросов: AVG(), COUNT(), SUM(), MAX(), MIN() и др.

Ещё один абстрактный пример подсчёта средней суммы:

rawQuery()

Также существует метод rawQuery(), принимающий сырой SQL-запрос.

Метод rawQuery() — это сырой запрос, как есть, т.е. пишется строка запроса, как это обычно делается в SQL. Пример выглядит следующим образом:

Класс SQLiteQueryBuilder — удобный способ для построения запросов. Выбор за вами!

Метод insert()

Для вставки новой записи в базу данных SQLite используется метод insert():

В метод insert() необходимо передать три параметра:

  • table — имя таблицы, в которую будет вставлена запись;
  • nullColumnHack — в базе данных SQLite не разрешается вставлять полностью пустую строку, и если строка, полученная от клиента контент-провайдера, будет пустой, то только этому столбцу явно будет назначено значение null;
  • values — карта отображений (класс Map и его наследники), передаваемая клиентом контент-провайдера, которая содержит пары ключ-значение. Ключи в карте должны быть названиями столбцов таблицы, значения — вставляемыми данными.

Метод insert() возвращает идентификатор _id вставленной строки или -1 в случае ошибки.

Для создания новой строки понадобится объект ContentValues, точнее, его метод put(), чтобы обеспечить данными каждый столбец. Объект ContentValues представляет собой пару name/value данных. Вставьте новую строку, передавая в метод insert(), вызванный в контексте нужной нам базы данных, имя таблицы и объект ContentValues

Метод update()

Для обновления и удаления записей в базе данных используют соответственно методы update() и delete():

В этих методах два последних параметра формируют SQL-выражение WHERE аналогично рассмотренному методу query() для чтения данных. Эти методы возвращают число модифицированных или удаленных строк.

Обновление строк также происходит с помощью класса ContentValues. Создайте новый объект ContentValues, используя метод put() для вставки значений в каждый столбец, который вы хотите обновить. Вызовите метод update() в контексте базы данных, передайте ему имя таблицы, обновленный объект ContentValues и оператор WHERE, указывающий на строку (строки), которую нужно обновить.

Ещё пример с использованием выражения WHERE:

В первом параметре метода мы указываем имя таблицы, которую хотим обновить.

Во втором параметре мы указываем значение, которое хотим обновить. Например, в колонке «DESCRIPTION» нужно обновить значение на «Clever».

В третьем параметре указывается условие для обновления. Выражение «NAME = ?» означает, что колонка NAME должна быть равна указанному значению. Знак вопроса является подстановочным символом и из четвёртого параметра берётся нужное значение.

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

Данный код соответствует выражению Where NAME = «Murzik» or DESCRIPTION = «Nice».

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

Если в последних двух параметрах использовать null, то будут обновлены все записи в таблице.

Метод delete()

Чтобы удалить строку, просто вызовите метод delete() в контексте базы данных, указав имя таблицы и оператор WHERE. В результате вы получите строки, которые хотите удалить:

Метод практически совпадает с методом update().

Удалим запись с условием WHERE NAME = «Murzik»

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

Метод openOrCreateDatabase: Открытие и создание баз данных без использования SQLiteOpenHelper

Вы можете открывать и создавать базы данных без помощи класса SQLiteOpenHelper, используя метод openOrCreateDatabase(), принадлежащий объекту Context вашего приложения. Получите доступ к базе данных в два шага. Сначала вызовите метод openOrCreateDatabase(), чтобы создать новую базу данных. Затем из полученного экземпляра базы данных вызовите execSQL(), чтобы выполнять команды на языке SQL, с помощью которых будут созданы таблицы и установлены отношения между ними.

Контент-провайдеры

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

  • создать собственный контент-провайдер, как подкласс класса ContentProvider;
  • добавить данные к существующему провайдеру — если уже есть провайдер, который управляет теми же данными, и вы имеете разрешение для работы с этими данными.

Вставляем картинки в базу

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

Создадим таблицу следующим образом. Переменная DATABASE_TABLE — строковая константа.

Источник

Оцените статью