Страница 1 из 5

Урок 101. Создаем свой ContentProvider

Добавлено: 06 авг 2012, 23:00
damager82
В этом уроке:
- создаем свой ContentProvider


Click here to read this article!

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 16 авг 2012, 11:21
neoksi
Спасибо, выцедил пару улучшений для своего ContentProvider'а, который был недавно мной написан.

У меня возник вопрос, в своем проекте я не могу использовать CursorAdapter, так как мне необходимо наполнять адаптер из нескольких курсоров. Возможно ли отловить событие обновления курсора и назначить свой обработчик? (к примеру как мы отлавливаем событие нажатия кнопки, но только для курсора).

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 27 авг 2012, 13:55
rezak90
Что то не въехал малёха. Не вижу большой разницы между ContentProvider'ом и SQLiteOpenHelper'ом. Всё что мы реализовываем (методы insert update delete и т.д.) можно реализовать и в SQLiteOpenHelper. Единственно что я понял так это что мы можем обратится из своего приложения к любой бд, я ж правильно понял?

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 27 авг 2012, 16:40
neoksi
rezak90
ContentProvider является посредником между данными (в нашем случае это БД) и приложениями. Так же он создается в единственном экземпляре, что дает следующие преимущества:
1) Открывается только одна копия источника данных (у нас это БД sqlite);
2) Позволяет сделать механизм синхронизации отображаемой информации и информации содержащийся в источнике.

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

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 27 авг 2012, 17:02
rezak90
короче я так понял провайдер даёт возможность подключаться к бд более чем одному приложению? Но не понял можно ли получить данные из бд которую не знаешь (например указать имя бд и вытянуть от тудого все таблицы с полями, заведомо не зная названия таблиц и столбцов)? И ещё, можно ли присабачить провайдер и программу в одно приложение, а не как создавать в уроке.
ап, ещё вычитал что с помощу провайдеров могут общаться между собой приложения, было бы интересно на это посмотреть.

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 27 авг 2012, 18:12
neoksi
rezak90 писал(а):короче я так понял провайдер даёт возможность подключаться к бд более чем одному приложению?
Принципиально прав, но я бы сказал, что он дает подключиться к БД более чем одному процессу.
rezak90 писал(а):Но не понял можно ли получить данные из бд которую не знаешь (например указать имя бд и вытянуть от тудого все таблицы с полями, заведомо не зная названия таблиц и столбцов)?
Тут скорей всего вопрос в правильном построении SQL запроса.
rezak90 писал(а): И ещё, можно ли присабачить провайдер и программу в одно приложение, а не как создавать в уроке.
Лично у меня оно так и сделано в моем приложении, а ещё у меня запрещено другим приложениям, кроме моего, обращаться к моему контент провайдеру.
rezak90 писал(а): ап, ещё вычитал что с помощу провайдеров могут общаться между собой приложения, было бы интересно на это посмотреть.
Когда с ними разберешься, то такое можно организовать.

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 30 авг 2012, 10:07
rezak90
я так понял что getContentResolver() возвращает все провайдеры которые есть в Android'e. В общем как например вытащить все названия провайдеров которые функционируют на данный момент в системе, не зная их названия.

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 30 авг 2012, 14:32
neoksi
rezak90 писал(а):я так понял что getContentResolver() возвращает все провайдеры которые есть в Android'e. В общем как например вытащить все названия провайдеров которые функционируют на данный момент в системе, не зная их названия.
getContentResolver() - это интерфейс, что ищет нужного контент провайдера по Uri.

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 04 сен 2012, 10:49
rezak90
Уже хотел накатать сюда огромный пост по одной проблеме, как вдруг осенило))) Как то после каждого обращения к бд я всё время закрывал подключения с ней, а потом курсор получался с возвратом null и не мог понять почему так. Такой себе вопрос для рассуждения: почему каждый раз открывается новое соединение с бд и не закрывается? Не влияет ли это на производительность? И вообще правильно ли это с точки зрения архитектуры? Или я может чего то не уловил...

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 04 сен 2012, 11:34
rezak90
всё время выходит путанница с айди. Вот у меня такой адрес "ua.my.providers.Supershop/sneakers/" где sneakers - название таблицы. Если обращаюсь по адресу "ua.my.providers.Supershop/sneakers/33" то uMatcher.match(uri) возвращает мне цифру 2 всегда, мне почему то казалось что метод match возвращает то что стоит в конце или же после названия таблицы, он берёт от куда то двойку. Сам UriMatcher такой:

Код: Выделить всё

uMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uMatcher.addURI(AUTHORITY, TABLE_NAME, 1);
		uMatcher.addURI(AUTHORITY, TABLE_NAME + "/#", 2);

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 04 сен 2012, 12:49
neoksi
rezak90 писал(а):всё время выходит путанница с айди. Вот у меня такой адрес "ua.my.providers.Supershop/sneakers/" где sneakers - название таблицы. Если обращаюсь по адресу "ua.my.providers.Supershop/sneakers/33" то uMatcher.match(uri) возвращает мне цифру 2 всегда, мне почему то казалось что метод match возвращает то что стоит в конце или же после названия таблицы, он берёт от куда то двойку. Сам UriMatcher такой:

Код: Выделить всё

uMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uMatcher.addURI(AUTHORITY, TABLE_NAME, 1);
		uMatcher.addURI(AUTHORITY, TABLE_NAME + "/#", 2);
Он тебе правильно возвращает 2.
В методе ты делаешь:

Код: Выделить всё

            // Вычисляем какая таблица нужна. 
	    switch (uriMatcher.match(uri)){
			case 1: 
				uTable = TABLE_NAME;
                        break;
			case 2: 
				uTable = TABLE_NAME;
				// Получаем id необходимой строки				
				uID = uri.getLastPathSegment();
                        break;
             }
И таким образом получаешь нужный ID.

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 04 сен 2012, 13:23
rezak90
а понял, ошибка была в самом запросе, нужно было не "ua.my.providers.Supershop/sneakers/33" а просто "ua.my.providers.Supershop/sneakers", то есть не ставить палку после названия таблицы, а так UriMatcher определял как айди столбца, а не всю таблицу.

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 17 ноя 2012, 16:16
htk
У меня возник вопрос, на который я никак не могу найти ответ, у меня довольно большая структура БД, и порядка 15 таблиц, мне необходимо писать для каждой своего провайдера, либо лепить все в один, и использовать кейсы?

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 15 фев 2013, 10:02
Ant
и ещё один вопрос: если кто-то сможет разобраться с запросами к моему провайдеру, то без проблем сможет курочить мою БД своим приложением? как от этого защищаться?

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 06 мар 2013, 11:02
Dangreon
Я не знаю, заметили или нет те кто смотрел этот урок, в отличие от SQLiteOpenHelper'а, обращение к базе при помощи своего ContentProvider'а позволяет
делать оповещения о сделанных изменениях. Эти оповещения в дальнейшем можно обрабатывать. Код где оповещения:
getContext().getContentResolver().notifyChange(uri, null);

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 25 апр 2013, 09:01
gh-gh
"У нас тут получилось, что имя таблицы в БД совпало с path в Uri. Это вовсе необязательно, они могут быть разными."
Если не ошибаюсь, если они будут разными, то в вашем примере будет IllegalArgumentException("Wrong URI: " + uri);
Потому что uri придет вида "content://ru.startandroid.providers.AdressBook/contacts" исходя из этой строчки
public static final Uri CONTACT_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + CONTACT_PATH);
а UriMatcher будет сравнивать её с чем нибудь вроде "content://ru.startandroid.providers.AdressBook/contacts_db" изходя из этого:
uriMatcher.addURI(AUTHORITY, CONTACT_TABLE, URI_CONTACTS);
Или я не правильно что то понял, но у меня не работает.

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 29 апр 2013, 11:23
damager82
gh-gh писал(а):"У нас тут получилось, что имя таблицы в БД совпало с path в Uri. Это вовсе необязательно, они могут быть разными."
Если не ошибаюсь, если они будут разными, то в вашем примере будет IllegalArgumentException("Wrong URI: " + uri);
Верно, у меня ошибка в уроке. Вместо
[syntax=java] uriMatcher.addURI(AUTHORITY, CONTACT_TABLE, URI_CONTACTS);
uriMatcher.addURI(AUTHORITY, CONTACT_TABLE + "/#", URI_CONTACTS_ID);[/syntax]
Надо
[syntax=java] uriMatcher.addURI(AUTHORITY, CONTACT_PATH, URI_CONTACTS);
uriMatcher.addURI(AUTHORITY, CONTACT_PATH+ "/#", URI_CONTACTS_ID);[/syntax]

Спасибо, что написали об этом!

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 30 апр 2013, 10:57
Mill666
Здравствуйте =)
вроде бы все делаю по уроку, однако программа при запуске сразу выдает ошибку =(

Код: Выделить всё

04-30 14:53:15.150: D/dalvikvm(2455): GC_CONCURRENT freed 56K, 7% free 2803K/3004K, paused 18ms+5ms, total 72ms
04-30 14:53:15.150: D/AndroidRuntime(2455): Shutting down VM
04-30 14:53:15.150: W/dalvikvm(2455): threadid=1: thread exiting with uncaught exception (group=0x40a71930)
04-30 14:53:15.170: E/AndroidRuntime(2455): FATAL EXCEPTION: main
04-30 14:53:15.170: E/AndroidRuntime(2455): java.lang.RuntimeException: Unable to start activity ComponentInfo{ru.startandroid.develop.contprovclient/ru.startandroid.develop.contprovclient.MainActivity}: java.lang.SecurityException: Permission Denial: opening provider ru.startandroid.develop.contentprovider.MyContactProvider from ProcessRecord{41718cd8 2455:ru.startandroid.develop.contprovclient/u0a10047} (pid=2455, uid=10047) that is not exported from uid 10046
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.access$600(ActivityThread.java:141)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Handler.dispatchMessage(Handler.java:99)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Looper.loop(Looper.java:137)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.main(ActivityThread.java:5041)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at java.lang.reflect.Method.invokeNative(Native Method)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at java.lang.reflect.Method.invoke(Method.java:511)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at dalvik.system.NativeStart.main(Native Method)
04-30 14:53:15.170: E/AndroidRuntime(2455): Caused by: java.lang.SecurityException: Permission Denial: opening provider ru.startandroid.develop.contentprovider.MyContactProvider from ProcessRecord{41718cd8 2455:ru.startandroid.develop.contprovclient/u0a10047} (pid=2455, uid=10047) that is not exported from uid 10046
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Parcel.readException(Parcel.java:1425)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Parcel.readException(Parcel.java:1379)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2545)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.acquireProvider(ActivityThread.java:4462)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2002)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1101)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.content.ContentResolver.query(ContentResolver.java:356)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.content.ContentResolver.query(ContentResolver.java:315)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at ru.startandroid.develop.contprovclient.MainActivity.onCreate(MainActivity.java:29)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.Activity.performCreate(Activity.java:5104)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	... 11 more


Re: Урок 101. Создаем свой ContentProvider

Добавлено: 17 май 2013, 16:50
exFoxer
Mill666 писал(а):Здравствуйте =)
вроде бы все делаю по уроку, однако программа при запуске сразу выдает ошибку =(

Код: Выделить всё

04-30 14:53:15.150: D/dalvikvm(2455): GC_CONCURRENT freed 56K, 7% free 2803K/3004K, paused 18ms+5ms, total 72ms
04-30 14:53:15.150: D/AndroidRuntime(2455): Shutting down VM
04-30 14:53:15.150: W/dalvikvm(2455): threadid=1: thread exiting with uncaught exception (group=0x40a71930)
04-30 14:53:15.170: E/AndroidRuntime(2455): FATAL EXCEPTION: main
04-30 14:53:15.170: E/AndroidRuntime(2455): java.lang.RuntimeException: Unable to start activity ComponentInfo{ru.startandroid.develop.contprovclient/ru.startandroid.develop.contprovclient.MainActivity}: java.lang.SecurityException: Permission Denial: opening provider ru.startandroid.develop.contentprovider.MyContactProvider from ProcessRecord{41718cd8 2455:ru.startandroid.develop.contprovclient/u0a10047} (pid=2455, uid=10047) that is not exported from uid 10046
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.access$600(ActivityThread.java:141)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Handler.dispatchMessage(Handler.java:99)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Looper.loop(Looper.java:137)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.main(ActivityThread.java:5041)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at java.lang.reflect.Method.invokeNative(Native Method)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at java.lang.reflect.Method.invoke(Method.java:511)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at dalvik.system.NativeStart.main(Native Method)
04-30 14:53:15.170: E/AndroidRuntime(2455): Caused by: java.lang.SecurityException: Permission Denial: opening provider ru.startandroid.develop.contentprovider.MyContactProvider from ProcessRecord{41718cd8 2455:ru.startandroid.develop.contprovclient/u0a10047} (pid=2455, uid=10047) that is not exported from uid 10046
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Parcel.readException(Parcel.java:1425)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.os.Parcel.readException(Parcel.java:1379)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2545)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.acquireProvider(ActivityThread.java:4462)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2002)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1101)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.content.ContentResolver.query(ContentResolver.java:356)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.content.ContentResolver.query(ContentResolver.java:315)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at ru.startandroid.develop.contprovclient.MainActivity.onCreate(MainActivity.java:29)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.Activity.performCreate(Activity.java:5104)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
04-30 14:53:15.170: E/AndroidRuntime(2455): 	... 11 more

У меня была похожая ошибка.
Помогло добавить в тег <provider> манифеста строку:
android:exported="true"

Мой тег <provider> теперь выглядит так:
<provider
android:name="MyContactsProvider"
android:authorities="ru.startandroid.providers.AdressBook"
android:exported="true" >
</provider>

Re: Урок 101. Создаем свой ContentProvider

Добавлено: 16 сен 2013, 14:51
Oleg_Sl
Ant писал(а):и ещё один вопрос: если кто-то сможет разобраться с запросами к моему провайдеру, то без проблем сможет курочить мою БД своим приложением? как от этого защищаться?
В AndroidManifest.xml указываем exported = false. При этом провайдер должен быть объявлен в самом приложении, а не как в уроке отдельно.

Код: Выделить всё

<provider
            android:name="[i]provider_name[/i]"
            android:authorities="[i]provider_authorities[/i]"
            android:exported="false">
       </provider>
Цитата из доков:
android:exported
Whether the content provider is available for other applications to use:
true: The provider is available to other applications. Any application can use the provider's content URI to access it, subject to the permissions specified for the provider.
false: The provider is not available to other applications. Set android:exported="false" to limit access to the provider to your applications. Only applications that have the same user ID (UID) as the provider will have access to it.
The default value is "true" for applications that set either android:minSdkVersion or android:targetSdkVersion to "16" or lower. For applications that set either of these attributes to "17" or higher, the default is "false".

You can set android:exported="false" and still limit access to your provider by setting permissions with the permission attribute.
Значение свойства exported определяет, доступен ли provider из других приложений.
true: provider доступен из других приложений через URI.
false: provider не доступен из других приложений. При это доступ из приложений с одинаковым user ID (подпись автора???) сохраняется.
Самое интересное: для приложений с API <= 16 значение по умолчанию "true", для API > 16 -- "false".

Источник: http://developer.android.com/guide/topi ... ement.html.