Vnd android cursor dir

Content Provider in Android — Basics

Aug 26, 2017 · 3 min read

Content Provider in Android — In our series of Android articles, Previously we discussed about Activity , Services and Fragment. Here we are going to discuss about content providers and to cover the basic concepts you need to make use of existing ones or to write content providers on your own.

What are Content Providers?

A C ontent Provider component supplies data fro m one application to others on request. Such requests are handled by the methods of the ContentResolver class. A content provider can use different ways to store its data and the data can be stored in files, in a database or even over a network.

Content Providers support the four basic operations, normally called CRUD-operations. CRUD is the acronym for create, read, update and delete. With content providers those objects simply represent data as most often a record of a database — but they could also be a photo on your SD-card or a video on the web.

Content URIs

The most important concept to understand when dealing with content providers is the content URI. Whenever you want to access data from a content provider you have to specify a URI. URIs for content providers look like this:

Here, detailed explanation about Content URIs

  • The scheme for content providers is always “ content”. The colon and double-slash “://” are a fixed part
  • This specifies the name of the content provider, for example browser , contacts etc. Authorities have to be unique for every content provider. Android documentation recommends to use the fully qualified class name of your ContentProvider-subclass.
  • The optional path, is used to distinguish the kinds of data your content provider offers.For example, distinguishes between audio files, video files and images using different paths for each of these types of media. This way a content provider can support different types of data that are nevertheless related. For totally unrelated data though you should use different content providers — and thus different authorities.
  • The last element is the optional id, which — if present — must be numeric. The id is used whenever you want to access a single record (e.g. a specific video file).

There are two types of URIs:

  • directory-based URIs
  • id-based URIs.

If no id is specified a URI is automatically a directory-based URI.

If You use directory-based URIs to access multiple elements of the same type (e.g. all songs of a band). All CRUD-operations are possible with directory-based URIs.

You use id-based URIs if you want to access a specific element. You cannot create objects using an id-based URI — but reading, updating and deleting is possible.

Content Types

A content type consist of a two types such as media type and a subtype divided by a slash. A typical example is “ image/png”. The media type “image” describes the content as an image file which is further specified to be of the Portable Network Graphic variety by the subtype “png”.

vnd.android.cursor.item — Used for single records

vnd.android.cursor.dir — Used for multiple records

The subtype on the other hand is used for content provider specific details and should differ for all types your content provider supports. The naming convention for subtypes is vnd.companyname.contenttype. Most content providers support multiple subtypes.

Which standard Content Providers are available?

A number of content providers are part of Android’s API. All these standard providers are defined in the package android.provider. The below lists the standard providers and what they are used for

  • CalendarContract SDK 14 — Manages the calendars on the user’s device.
  • Browser SDK 1 — Manages your web-searches, bookmarks and browsing-history.
  • CallLog SDK 1 — Keeps track of your call history.
  • MediaStore SDK 1 — The content provider responsible for all your media files like music, video and pictures.
  • Settings SDK 1 — Manages all global settings of your device.
  • UserDictionary SDK 3 — Keeps track of words you add to the default dictionary.
Читайте также:  Мультимедиа андроид для тойота камри

Conclusion:

In this part of this mini series you have learned all about the two types of URIs for content providers as well as about the two media types of content types.

News Flash: Google’s Latest update was named as Android Oreo

Источник

Полный список

— создаем свой ContentProvider

Content Provider – это способ расшарить для общего пользования данные вашего приложения. Обычно это данные из БД. И создание класса провайдера похоже на создание обычного класса для работы с БД. Мы используем SQLiteOpenHelper для управления базой, и реализуем методы query, insert, update, delete класса ContentProvider.

Но есть и отличия. При работе с провайдером используется Uri. Он составной и похож на http-адрес. С помощью Uri система понимает, какой именно провайдер нужен, c какими данными необходимо работать и с какой конкретно записью. Uri можно представить в таком виде: content:// / / .

Например, возьмем Uri — content://ru.startandroid.provider.AdressBook/contacts/7 и разложим на части:

content:// — это стандартное начало для адреса провайдера.

ru.startandroid.provider.AdressBook– это authority. Определяет провайдера (если проводить аналогию с базой данных, то это имя базы).

contacts – это path. Какие данные от провайдера нужны (таблица).

7 – это ID. Какая конкретно запись нужна (ID записи)

path может быть составным – например contacts/phones или contacts/email. Это используется, если структура данных достаточно обширна, и данные хранятся в нескольких таблицах в соответствии с некоторой логикой и организацией.

ID может быть не указан. Это означает, что будем работать со всеми записями из path.

Вышерассмотренный пример Uri указывает системе, что мы хотим достучаться до провайдера адресной книги ru.startandroid.provider.AdressBook, и получить доступ к контакту с >

Попробуем создать свой провайдер. Пусть это будет некая адресная книга – список контактов. Для каждого контакта будем хранить всего два атрибута: имя и емэйл.

И отдельно создадим приложение, которое будет к этому провайдеру обращаться и манипулировать данными – читать, добавлять, изменять, удалять.

Начнем с провайдера. Создадим проект без Activity:

Project name: P1011_ContentProvider
Build Target: Android 2.3.3
Application name: ContentProvider
Package name: ru.startandroid.develop.p1011contentprovider

Создаем класс MyContactsProvider, наследующий android.content.ContentProvider

Он предлагает нам реализовать кучу методов. Реализуем.

Кода много, но практически ничего нового для нас нет. В основном идет работа с БД.

В начале идет куча констант. Константы для БД должны быть знакомы и понятны по прошлым урокам, их я не объясняю. Поясню только, что у нас в БД будет всего одна таблица contacts с тремя полями: _id, name и email.

Далее идут константы AUTHORITY и CONTACT_PATH – это составные части Uri. Мы это уже обсуждали в начале урока. Из этих двух констант и префикса content:// мы формируем общий Uri — CONTACT_CONTENT_URI. Т.к. здесь не указан никакой ID, этот Uri дает доступ ко всем контактам.

У нас тут получилось, что имя таблицы в БД совпало с path в Uri. Это вовсе необязательно, они могут быть разными.

Далее описываем MIME-типы данных, предоставляемых провайдером. Один для набора данных, другой для конкретной записи. У меня пока что мало опыта работы с провайдерами, и я не очень понимаю, где и как можно эти типы данных использовать. Но реализовать их надо, поэтому делаем это. Мы будем возвращать их в методе getType нашего провайдера.

Далее создаем и описываем UriMatcher и константы для него. UriMatcher – это что-то типа парсера. В методе addURI мы даем ему комбинацию: authority, path и константа. Причем, мы можем использовать спецсимволы: * — строка любых символов любой длины, # — строка цифр любой длины. На вход провайдеру будут поступать Uri, и мы будем сдавать их в UriMatcher на проверку. Если Uri будет подходить под комбинацию authority и path, ранее добавленных в addURI, то UriMatcher вернет константу из того же набора: authority, path, константа.

uriMatcher.addURI(AUTHORITY, CONTACT_PATH, URI_CONTACTS);

означает, что мы добавили в uriMatcher комбинацию значений AUTHORITY, CONTACT_PATH и URI_CONTACTS.

uriMatcher.addURI(AUTHORITY, CONTACT_PATH + «/#», URI_CONTACTS_ID);

означает, что мы добавили в uriMatcher комбинацию значений AUTHORITY, CONTACT_PATH + «/#» и URI_CONTACTS_ID. # — это маска для строки из цифр. А если у нас к path добавляется число, это значит — нам дают ID и мы будем работать с конкретной записью.

И теперь, если мы попросим uriMatcher проверить Uri, состоящий из AUTHORITY и CONTACT_PATH, он вернет нам значение URI_CONTACTS. А если дадим ему Uri, состоящий из AUTHORITY, CONTACT_PATH и числа (ID), то он вернет нам URI_CONTACTS_ID. А мы по этим константам определим – работать со всеми записями или какой-то конкретной.

Читайте также:  Создание печатных плат для андроид

В общем, на словах все очень сложно получилось, в коде все проще будет. Но главный смысл этого uriMatcher в том, что он определит, какой Uri к нам пришел – общий или с ID. Если общий – то вернет URI_CONTACTS, если с ID – то вернет URI_CONTACTS_ID.

В OnCreate создаем DBHelper – уже знакомый нам помощник для работы с БД.

В методе query мы получаем на вход Uri и набор параметров для выборки из БД: projection — столбцы, selection — условие, selectionArgs – аргументы для условия, sortOrder – сортировка. Опять же, эти параметры уже знакомы нам по работе с БД.

Далее мы отдаем uri в метод match объекта uriMatcher. Он его разбирает, сверяет с теми комбинациями authority/path, которые мы давали ему в методах addURI и выдает константу из соответствующей комбинации. Если это URI_CONTACTS, значит нам пришел общий Uri и от провайдера хотят получить все его записи. В этом случае мы проверим, указана ли сортировка. Если нет, то поставим сортировку по имени. Как вы понимаете, эта операция с сортировкой совершенно необязательна. Мы могли и ничего не делать. Если же мы получили URI_CONTACTS_ID, то провайдер должен вернуть запись по конкретному ID. Для этого мы извлекаем ID из Uri методом getLastPathSegment и добавляем его в условие selection.

Если uriMatcher не смог опознать Uri, то будем выдавать IllegalArgumentException. Вы, разумеется, можете тут прописать свое решение этой проблемы.

Далее мы получаем БД и выполняем для нее метод query, получаем cursor. Регистрируем этот cursor, чтобы он получал уведомления, когда будут меняться данные, соответствующие общему Uri — CONTACT_CONTENT_URI. При изменении какой-либо конкретной записи, уведомление также будет срабатывать. В конце возвращаем cursor.

В insert мы проверяем, что нам пришел наш общий Uri. Если все ок, то вставляем данные в таблицу, получаем ID. Этот ID мы добавляем к общему Uri и получаем Uri с ID. По идее, это можно сделать и обычным сложением строк, но рекомендуется использовать метод withAppendedId объекта. Далее мы уведомляем систему, что поменяли данные, соответствующие resultUri. Система посмотрит, не зарегистрировано ли слушателей на этот Uri. Увидит, что мы регистрировали курсор, и даст ему знать, что данные обновились. В конце мы возвращаем resultUri, соответствующий новой добавленной записи.

В delete мы проверяем, какой Uri нам пришел. Если с ID, то фиксим selection – добавляем туда условие по ID. Выполняем удаление в БД, получаем кол-во удаленных записей. Уведомляем, что данные изменились. Возвращаем кол-во удаленных записей.

В update мы проверяем, какой Uri нам пришел. Если с ID, то фиксим selection – добавляем туда условие по ID. Выполняем обновление в БД, получаем кол-во обновленных записей. Уведомляем, что данные изменились. Возвращаем кол-во обновленных записей.

В методе getType возвращаем типы соответственно типу Uri – общий или с ID.

Класс DBHelper помогает нам создать БД и наполнить ее первоначальными данными. Обновление здесь не реализуем.

Осталось прописать класс провайдера в манифесте. Делается это аналогично, как мы прописываем Activity или сервис. Только из списка выбираем Provider. В Name выбираем наш класс. И заполняем поле Authorities, сюда необходимо прописать значение из константы AUTHORITY — ru.startandroid.providers.AdressBook.

Теперь, когда система получит запрос на получение данных по Uri с authority = ru.startandroid.providers.AdressBook, она будет работать с нашим провайдером.

С провайдером все. Его можно инсталлить на AVD. Делается это как обычно, просто на экране ничего не появится, т.к. нет Activity. А в консоли будут примерно такие строки:

Uploading P1011_ContentProvider.apk onto device ’emulator-5554′
Installing P1011_ContentProvider.apk.
Success!
\P1011_ContentProvider\bin\P1011_ContentProvider.apk installed on device
Done!

Теперь пишем приложение, которое будет к провайдеру обращаться. Создадим проект:

Project name: P1012_ContProvClient
Build Target: Android 2.3.3
Application name: ContProvClient
Package name: ru.startandroid.develop.p1012contprovclient
Create Activity: MainActivity

Добавим в strings.xml строки:

4 кнопки для операций с данными и список для вывода данных провайдера.

В CONTACT_URI мы храним общий Uri. В CONTACT_NAME и CONTACT_EMAIL – имена полей.

В onCreate мы используем метод getContentResolver, чтобы получить ContentResolver. Этот объект – посредник между нами и провайдером. Мы вызываем его метод query и передаем туда Uri. Остальные параметры оставляем пустыми – т.е. нам вернутся все записи, все поля и сортировку мы не задаем. Полученный курсор мы передаем в Activity на управление – метод startManagingCursor. Далее создаем адаптер и присваиваем его списку.

Читайте также:  Камера с режимом ночной съемки андроид

В onClickInsert мы используем метод insert для добавления записей в провайдер. Этот метод возвращает нам Uri, соответствующий новой записи.

В onClickUpdate мы создаем Uri, соответствующий записи с и апдейтим эту запись в провайдере.

В onClickDelete мы создаем Uri, соответствующий записи с и удаляем эту запись в провайдере.

В onClickError мы пытаемся получить записи по Uri, который не знает провайдер. В его uriMatcher не добавляли информации об этом Uri. В этом случае мы генерировали в провайдере ошибку. Здесь попробуем поймать ее.

Все сохраняем и запускаем приложение.

onCreate
query, content://ru.startandroid.providers.AdressBook/contacts
URI_CONTACTS

Создался провайдер. Выполнился его метод query и получил на вход Uri — content://ru.startandroid.providers.AdressBook/contacts. uriMatcher вернул URI_CONTACTS, т.е. опознал Uri – как общий, запрашивающий все данные. В итоге мы получили курсор со всеми данными и вывели их в список.

Жмем Insert. Появилась новая строка в списке.

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

insert, content://ru.startandroid.providers.AdressBook/contacts
insert, result Uri : content://ru.startandroid.providers.AdressBook/contacts/4

Выполнился метод insert, получил на вход общий Uri, в котором указано, в какую именно таблицу вставлять данные. Данные добавлены и провайдер вернул Uri новой записи: content://ru.startandroid.providers.AdressBook/contacts/4

Мы обновили вторую запись, и она ушла в конец списка, т.к. сортировку мы еще в провайдере настроили по имени, если не указано иное.

update, content://ru.startandroid.providers.AdressBook/contacts/2
URI_CONTACTS_ID, 2
update, count = 1

Сработал метод update, получил на вход Uri = content://ru.startandroid.providers.AdressBook/contacts/2. UriMatcher верно распознал, что полученный Uri содержит ID. Провайдер обновил запись и вернул нам кол-во обновленных записей.

Удалили запись с >

delete, content://ru.startandroid.providers.AdressBook/contacts/3
URI_CONTACTS_ID, 3
delete, count = 1

Сработал метод delete, получил на вход Uri = content://ru.startandroid.providers.AdressBook/contacts/3. UriMatcher определил, что Uri с ID. Запись была удалена и мы получили кол-во удаленных записей.

query, content://ru.startandroid.providers.AdressBook/phones
Error: class java.lang.IllegalArgumentException, Wrong URI: content://ru.startandroid.providers.AdressBook/phones

Был выполнен метод query с Uri = content://ru.startandroid.providers.AdressBook/phones. Но UriMatcher не знает такую комбинацию authority (ru.startandroid.providers.AdressBook) и path (phones). В этой ситуации мы настроили провайдер так, что он генерит ошибку. В приложении мы эту ошибку ловим и выдаем в лог.

Есть несколько моментов, которые хотелось бы отдельно отметить.

managedQuery

Мы в приложении использовали методы query и startManagingCursor. У Activity есть метод, который объединяет два этих метода — managedQuery. Он берет на вход те же параметры, что и query и возвращает курсор, который уже находится под управлением Activity.

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

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

getWritableDatabase

Метод getWritableDatabase по причинам производительности не рекомендуется вызывать в onCreate методе провайдера. Поэтому мы в onCreate только создавали DBHelper, а в методах query, insert и прочих вызывали getWritableDatabase() и получали доступ к БД.

Условия выборки

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

Более подробную инфу об этом всем можно найти на офиц.сайте.

На следующем уроке:

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

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