Пример создания ContentProvider для работы с базой SQLite — часть 1

Часть 2
Часть 3

Согласно официальным руководствам от команды Android SDK, ContentProvider необходим лишь в случаях, когда вашему приложению необходимо предоставлять свои данные для других сторонних приложений, или, к примеру, реализовать поиск среди данных вашего приложения с использованием search suggestions (подсказки при вводе слов для поиска).

Тем не менее, для отображения массивов данных, приходится часто использовать CursorAdapter совместно с CursorLoader. Данная связка облегчает работу с различными списками и таблицами, потому что автоматически наблюдает за изменениями в базе sqlite и обновляет данные, когда это необходимо.

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

Students

_id first_name second_name average_score fk_class_id

Classes

_id class_number class_letter

fk_class_id будет являться внешним ключом к классу Classes и хранить значение Classes._id

Теперь, когда мы знаем схему нашей базы sqlite, можно создать так называемый Contract Class — класс, который будет хранить все имена столбцов, проекции для запросов и остальные стандартные (для наших таблиц) значения. Сам класс выглядит так:

public final class ContractClass {
	public static final String AUTHORITY = "org.nerdgrl.examples.contentproviderexample.provider.ContractClass";
	private ContractClass() {}
	public static final class Students implements BaseColumns {...}
	public static final class Classes implements BaseColumns {...}
}

где AUTHORITY — это уникальное имя приложения, которое будут использовать другие приложения, если вы решите предоставить для них свои внутренние данные из БД. Чтобы не было конфликтов с другими провайдерами, рекомендуют использовать доменное имя в качестве префикса.

Класс BaseColumns представляет собой список необходимых для работы с базой sqlite столбцов, а именно содержит константы _ID и _COUNT. Хотя ContentProvider не требует чтобы первичный ключ назывался _ID, для работы с ListView, к примеру, ключ _ID необходим. Поэтому проще использовать класс BaseColumns, где оба эти столбца уже есть и вы о них не забудете.

Теперь разберемся с классом Students:

public static final class Students implements BaseColumns {
	private Students() {}
	public static final String TABLE_NAME ="students";
	private static final String SCHEME = "content://";
	private static final String PATH_STUDENTS = "/students";
	private static final String PATH_STUDENTS_ID = "/students/";
	public static final int STUDENTS_ID_PATH_POSITION = 1;
	public static final Uri CONTENT_URI =  Uri.parse(SCHEME + AUTHORITY + PATH_STUDENTS);
	public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME + AUTHORITY + PATH_STUDENTS_ID);
	public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.students";
	public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.students";
	public static final String DEFAULT_SORT_ORDER = "second_name ASC";
	public static final String COLUMN_NAME_FIRST_NAME   = "first_name";
	public static final String COLUMN_NAME_SECOND_NAME   = "second_name";
	public static final String COLUMN_NAME_AVERAGE_SCORE   = "average_score";
	public static final String COLUMN_NAME_FK_CLASS_ID   = "fk_class_id";
	public static final String[] DEFAULT_PROJECTION = new String[] {
		ContractClass.Students._ID,
		ContractClass.Students.COLUMN_NAME_FIRST_NAME,
		ContractClass.Students.COLUMN_NAME_SECOND_NAME,
		ContractClass.Students.COLUMN_NAME_AVERAGE_SCORE,
		ContractClass.Students.COLUMN_NAME_FK_CLASS_ID
	};
}

Мы видим приватный конструктор, который запрещает неправильное использование данного класса, ведь нам нужны только константы.

TABLE_NAME — имя таблицы в базе sqlite.

SCHEME — схема данных, которую указывают, например, в <intent-filter><data scheme=…></intent-filter> . Название чувствительно к регистру, поэтому рекомендуется использовать только нижний.

PATH_STUDENTS и PATH_STUDENTS_ID — используются для составления Content Uri. ContentProvider рассматривает Content Uri как путь к нужной таблице, и/или к конкретной записи.
Content Uri для таблицы Students будет выглядеть следующим образом:
content://org.nerdgrl.examples.contentproviderexample.provider.ContractClass/students (для всей директории)
content://org.nerdgrl.examples.contentproviderexample.provider.ContractClass/students/3 (для записи под номером 3)

CONTENT_TYPE и CONTENT_ITEM_TYPE — это MIME типы для хранящихся в нашей БД данных. Все пользовательские типы данных должны начинаться с vnd.android.cursor.dir/vnd для директорий, и с vnd.android.cursor.item/vnd для отдельных записей.

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

DEFAULT_PROJECTION — это набор столбцов, которые будут включены в результаты запроса.

Код класса Classes:

public static final class Classes implements BaseColumns {
	private Classes() {}
	public static final String TABLE_NAME ="classes";
	private static final String SCHEME = "content://";
	private static final String PATH_CLASSES = "/classes";
	private static final String PATH_CLASSES_ID = "/classes/";
	public static final int CLASSES_ID_PATH_POSITION = 1;
	public static final Uri CONTENT_URI =  Uri.parse(SCHEME + AUTHORITY + PATH_CLASSES);
	public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME + AUTHORITY + PATH_CLASSES_ID);
	public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.org.nerdgrl.classes";
	public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.org.nerdgrl.classes";
	public static final String DEFAULT_SORT_ORDER = "class_number ASC";
	public static final String COLUMN_NAME_CLASS_NUMBER   = "class_number";
	public static final String COLUMN_NAME_CLASS_LETTER   = "class_letter";
	public static final String[] DEFAULT_PROJECTION = new String[] {
		ContractClass.Classes._ID,
		ContractClass.Classes.COLUMN_NAME_CLASS_NUMBER,
		ContractClass.Classes.COLUMN_NAME_CLASS_LETTER
	};
}

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

Полный исходный код можно найти здесь.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Translate »