O provedor de conteúdo gerencia o acesso a um repositório central de dados. Você implementa um
provedor como uma ou mais classes em um aplicativo Android, junto com elementos em
no arquivo de manifesto. Uma das classes implementa uma subclasse de
ContentProvider
, que é a interface entre seu provedor e
entre outros aplicativos.
Embora o objetivo dos provedores de conteúdo seja disponibilizar dados para outros aplicativos, é possível ter atividades em seu aplicativo que permitam ao usuário consultar e modificar os dados gerenciados pelo seu provedor.
Esta página contém o processo básico para criar um provedor de conteúdo e uma lista de APIs a serem usadas.
Antes de começar a criar
Antes de começar a criar um provedor, considere o seguinte:
-
Decida se você precisa de um provedor de conteúdo. Você precisa criar um conteúdo
se quiser fornecer um ou mais dos seguintes recursos:
- Oferecer dados ou arquivos complexos a outros aplicativos.
- Você quer permitir que os usuários copiem dados complexos do seu app para outros apps.
- Fornecer sugestões de pesquisa personalizada usando a biblioteca de pesquisa.
- Expor os dados do aplicativo a widgets.
- Você quer implementar o
AbstractThreadedSyncAdapter
,CursorAdapter
ouCursorLoader
classes.
Você não precisa de um provedor para usar bancos de dados ou outros tipos de armazenamento permanente se o uso for inteiramente dentro do seu próprio aplicativo e não precisa dos recursos listados acima. Em vez disso, você pode usar um dos sistemas de armazenamento descritos em Visão geral do armazenamento de dados e arquivos.
- Caso ainda não tenha feito isso, leia Fundamentos do provedor de conteúdo para saber mais sobre os provedores e como eles funcionam.
A seguir, siga estas etapas para criar seu provedor:
-
Projete o armazenamento bruto dos dados. Um provedor de conteúdo oferece dados de duas maneiras:
- Dados de arquivo
- Dados que normalmente transitam em arquivos, como fotos, áudio ou vídeos. Armazene os arquivos no ambiente particular espaço. Em resposta a uma solicitação de um arquivo por outro aplicativo, o provedor pode oferecer um identificador para o arquivo.
- "Estruturado" dados
- Dados que normalmente vão para um banco de dados, uma matriz ou uma estrutura semelhante. Armazene os dados em um formato compatível com tabelas de linhas e colunas. linha A representa uma entidade, como uma pessoa ou um item no inventário. Uma coluna representa alguns dados da entidade, como o nome de uma pessoa ou o preço de um item. Uma forma comum de armazenar esse tipo de dados é em um banco de dados SQLite, mas é possível usar qualquer tipo de armazenamento permanente. Para saber mais sobre os tipos de armazenamento disponíveis na sistema Android, consulte a Seção de armazenamento de dados de design.
-
Defina uma implementação concreta da classe
ContentProvider
e os métodos necessários. Essa classe é a interface entre seus dados e o restante sistema Android. Para mais informações sobre esta classe, consulte a Implemente a seção da classe ContentProvider. - Defina a string de autoridade do provedor, URIs de conteúdo e nomes de colunas. Se você quiser o aplicativo do provedor para tratar intents, também definir ações da intent, dados extras e sinalizações. Defina também as permissões necessárias para aplicativos que desejam para acessar seus dados. Considere definir todos esses valores como constantes em uma classe de contrato separada. Depois, é possível expor essa classe a outros desenvolvedores. Para mais informações sobre URIs de conteúdo, consulte a Seção Projetar URIs de conteúdo. Para saber mais sobre intents, consulte a seção Intents e acesso a dados.
-
Adicione outras partes opcionais, como dados de amostra ou uma implementação
de
AbstractThreadedSyncAdapter
que podem sincronizar dados entre o provedor e os dados baseados na nuvem.
Armazenamento de dados de design
Os provedores de conteúdo são a interface para dados salvos em um formato estruturado. Antes de criar interface, decida como armazenar os dados. Você pode armazenar os dados da forma que quiser e depois projetar a interface para ler e gravar os dados conforme necessário.
Estas são algumas das tecnologias de armazenamento de dados disponíveis no Android:
- Se você estiver trabalhando com dados estruturados, considere usar um banco de dados relacional, como como SQLite ou um repositório de dados de chave-valor não relacional, como LevelDB (em inglês). Se você estiver trabalhando com dados não estruturados, como mídia de áudio, imagem ou vídeo, considere armazenar dados como arquivos. É possível misturar e combinar vários tipos de armazenamentos e expô-los. usar um único provedor de conteúdo, se necessário.
-
O sistema Android pode interagir com a biblioteca de persistência Room, que
Fornece acesso à API do banco de dados SQLite que os provedores do Android
para armazenar dados orientados a tabelas. Para criar um banco de dados usando este
instanciar uma subclasse de
RoomDatabase
, conforme descrito nas Salve dados em um banco de dados local usando Room.Você não precisa usar um banco de dados para implementar seu repositório. Um fornecedor aparece externamente como um conjunto de tabelas, semelhante a um banco de dados relacional, mas essa não é um requisito para a implementação interna do provedor.
- Para armazenar dados de arquivos, o Android tem diversas APIs orientadas a arquivo. Para saber mais sobre o armazenamento de arquivos, leia Visão geral do armazenamento de dados e arquivos. Se você estiver projetar um provedor que oferece dados relacionados à mídia, como músicas ou vídeos, é possível e você tem um provedor que combina dados de tabelas e arquivos.
- Em casos raros, você pode se beneficiar da implementação de mais de um provedor de conteúdo para um único aplicativo. Por exemplo, você pode querer compartilhar alguns dados com um widget usando provedor de conteúdo e expor um conjunto diferente de dados para compartilhamento com outros aplicativos conteinerizados.
-
Para trabalhar com dados baseados em rede, use classes em
java.net
eandroid.net
. Também é possível sincronizar dados baseados em rede com um como um banco de dados e, em seguida, oferecer os dados na forma de tabelas ou arquivos.
Observação: se você fizer uma alteração no repositório que não esteja compatível com versões anteriores, será necessário marcar o repositório com uma nova versão número Também é necessário aumentar o número da versão do app que implementa o novo provedor de conteúdo. Essa mudança impede que o sistema faça downgrade do sistema, fazendo com que o sistema travessia ao tentar reinstalar um app com um provedor de conteúdo incompatível.
Considerações para projetar dados
Aqui estão algumas dicas para projetar a estrutura de dados do seu provedor:
-
Os dados da tabela sempre precisam ter uma "chave primária" que o provedor mantém
como um valor numérico único para cada linha. É possível usar esse valor para vincular a linha a
linhas em outras tabelas (usando-o como uma "chave estrangeira"). Embora seja possível usar qualquer nome
para esta coluna, usar
BaseColumns._ID
é o melhor escolha porque vincular os resultados de uma consulta de provedor a umListView
exige que uma das colunas recuperadas tenha o nome_ID
-
Se você quiser fornecer imagens bitmap ou outros dados muito grandes orientados a arquivos, armazene
os dados em um arquivo e, em seguida, fornecê-los indiretamente em vez de armazená-los diretamente em um
tabela. Se você fizer isso, precisará informar aos usuários do seu provedor que eles precisam usar uma
ContentResolver
para acessar os dados. -
Use o tipo de dados de objeto grande binário (BLOB) para armazenar dados que variam em tamanho ou que têm
com uma estrutura variada. Por exemplo, você pode usar uma coluna BLOB para armazenar um
buffer de protocolo ou
Estrutura JSON.
Também é possível usar um BLOB para implementar uma tabela independente de esquema. Em nesse tipo de tabela, você define uma coluna de chave primária, uma coluna de tipo MIME e uma ou mais colunas mais genéricas como BLOB. O significado dos dados nas colunas BLOB é indicado pelo valor na coluna de tipo MIME. Isso permite que você armazene diferentes tipos de linhas em na mesma tabela. Os "dados" do Provedor de contatos mesa
ContactsContract.Data
é um exemplo de um modelo independente de esquema tabela.
Projetar URIs de conteúdo
Um URI de conteúdo é um URI que identifica dados em um provedor. Os URIs de conteúdo incluem
o nome simbólico de todo o provedor (a autoridade dele) e um
nome que aponte para uma tabela ou arquivo (um caminho). A parte do ID opcional aponta para
uma linha individual em uma tabela. Todos os métodos de acesso aos dados
ContentProvider
tem um URI de conteúdo como argumento. Isso permite que você
determinar a tabela, linha ou arquivo para acessar.
Para saber mais sobre URIs de conteúdo, consulte Fundamentos do provedor de conteúdo.
Crie uma autoridade
Os provedores normalmente têm uma única autoridade, que serve como seu nome interno do Android. Para evitar conflitos com outros provedores, usar a propriedade do domínio da Internet (ao contrário) como base da autoridade do seu provedor. Como essa recomendação também é válida para dispositivos nomes de pacotes, é possível definir a autoridade do provedor como uma extensão do nome do pacote que contém o provedor.
Por exemplo, se o nome do seu pacote Android for
com.example.<appname>
, forneça ao seu provedor
com.example.<appname>.provider
.
Projetar uma estrutura de caminho
Os desenvolvedores geralmente criam URIs de conteúdo a partir da autoridade anexando caminhos que apontam para
em tabelas individuais. Por exemplo, se você tiver duas tabelas, table1 e
table2, será possível combiná-las com a autoridade do exemplo anterior para gerar o
URIs de conteúdo
com.example.<appname>.provider/table1
e
com.example.<appname>.provider/table2
. Os caminhos não são
é limitado a um único segmento, e não precisa haver uma tabela para cada nível do caminho.
Processar IDs de URI de conteúdo
Por convenção, provedores oferecem acesso a uma única linha em uma tabela aceitando um URI de conteúdo
com um valor de ID para a linha no fim do URI. Também por convenção, os provedores correspondem aos
ID do cliente à coluna _ID
da tabela e realizar o acesso solicitado na
correspondente.
Essa convenção facilita um padrão comum de projeto para aplicativos que acessam um provedor. O app
faz uma consulta no provedor e mostra o Cursor
resultante
em uma ListView
usando um CursorAdapter
.
A definição de CursorAdapter
requer uma das colunas na
Cursor
para _ID
Em seguida, o usuário seleciona uma das linhas exibidas na IU para ver ou modificar o
dados. O app recebe a linha correspondente do Cursor
que apoia a
ListView
, recebe o valor _ID
dessa linha e o anexa a
URI de conteúdo e envia a solicitação de acesso ao provedor. O provedor pode fazer o
ou modificação na linha exata que o usuário selecionou.
Padrões de URI de conteúdo
Para ajudar você a escolher qual ação realizar para um URI de conteúdo recebido, a API do provedor inclui
a classe de conveniência UriMatcher
, que mapeia padrões de URI de conteúdo para
valores inteiros. É possível usar os valores inteiros em uma instrução switch
que
escolhe a ação desejada para o URI de conteúdo ou URIs que correspondem a um padrão específico.
Um padrão de URI de conteúdo corresponde a URIs de conteúdo que usam caracteres curinga:
-
*
corresponde a uma string de caracteres válidos de qualquer tamanho. -
#
corresponde a uma string de caracteres numéricos de qualquer tamanho.
Como um exemplo de criação e codificação de tratamento de URI de conteúdo, considere um provedor com o
autoridade com.example.app.provider
que reconhece os seguintes URIs de conteúdo
apontando para tabelas:
-
content://com.example.app.provider/table1
: uma tabela chamadatable1
. -
content://com.example.app.provider/table2/dataset1
: uma tabela chamadadataset1
. -
content://com.example.app.provider/table2/dataset2
: uma tabela chamadadataset2
. -
content://com.example.app.provider/table3
: uma tabela chamadatable3
.
O provedor também reconhece esses URIs de conteúdo se eles tiverem um ID de linha anexado a eles, como content://com.example.app.provider/table3/1
para a linha identificada por
1
em table3
.
Os seguintes padrões de URI de conteúdo são possíveis:
-
content://com.example.app.provider/*
- Corresponde a qualquer URI de conteúdo no provedor.
-
content://com.example.app.provider/table2/*
-
Corresponde a um URI de conteúdo das tabelas
dataset1
edataset2
, mas não corresponde a URIs de conteúdo paratable1
outable3
. -
content://com.example.app.provider/table3/#
-
Corresponde a um URI de conteúdo
para linhas únicas em
table3
, comocontent://com.example.app.provider/table3/6
para a linha identificada por6
.
O snippet de código abaixo mostra como os métodos em UriMatcher
funcionam.
Esse código lida com URIs de uma tabela inteira de maneira diferente dos URIs de uma
linha única usando o padrão de URI de conteúdo
content://<authority>/<path>
para tabelas e
content://<authority>/<path>/<id>
para linhas únicas.
O método addURI()
mapeia uma
autoridade e caminho como um valor inteiro. O método match()
retorna o valor inteiro de um URI. Uma instrução switch
escolhe entre consultar a tabela inteira e consultar um único registro.
Kotlin
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used * in the path. */ addURI("com.example.app.provider", "table3", 1) /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ addURI("com.example.app.provider", "table3/#", 2) } ... class ExampleProvider : ContentProvider() { ... // Implements ContentProvider.query() override fun query( uri: Uri?, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { var localSortOrder: String = sortOrder ?: "" var localSelection: String = selection ?: "" when (sUriMatcher.match(uri)) { 1 -> { // If the incoming URI was for all of table3 if (localSortOrder.isEmpty()) { localSortOrder = "_ID ASC" } } 2 -> { // If the incoming URI was for a single row /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ localSelection += "_ID ${uri?.lastPathSegment}" } else -> { // If the URI isn't recognized, // do some error handling here } } // Call the code to actually do the query } }
Java
public class ExampleProvider extends ContentProvider { ... // Creates a UriMatcher object. private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to one. No wildcard is used * in the path. */ uriMatcher.addURI("com.example.app.provider", "table3", 1); /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ uriMatcher.addURI("com.example.app.provider", "table3/#", 2); } ... // Implements ContentProvider.query() public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... /* * Choose the table to query and a sort order based on the code returned for the incoming * URI. Here, too, only the statements for table 3 are shown. */ switch (uriMatcher.match(uri)) { // If the incoming URI was for all of table3 case 1: if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; break; // If the incoming URI was for a single row case 2: /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ selection = selection + "_ID = " + uri.getLastPathSegment(); break; default: ... // If the URI isn't recognized, do some error handling here } // Call the code to actually do the query }
Outra classe, ContentUris
, oferece métodos práticos para trabalhar
com a parte id
dos URIs de conteúdo. As classes Uri
e
As Uri.Builder
incluem métodos convenientes para analisar
Uri
e a criação de novos.
Implementar a classe ContentProvider
A instância ContentProvider
gerencia o acesso
a um conjunto estruturado de dados processando solicitações de outros aplicativos. Todos os formulários
de acesso, em algum momento, chamará ContentResolver
, que, em seguida, chamará uma
de ContentProvider
para ter acesso.
Métodos obrigatórios
A classe abstrata ContentProvider
define seis métodos abstratos que
que você implementa como parte da subclasse concreta. Todos esses métodos, exceto
onCreate()
são chamados por um aplicativo cliente
que está tentando acessar seu provedor de conteúdo.
-
query()
-
Recupere dados do provedor. Use os argumentos para selecionar a tabela para
consulta, as linhas e colunas a serem retornadas e a ordem de classificação do resultado.
Retorne os dados como um objeto
Cursor
. -
insert()
- Insira uma nova linha no provedor. Use os argumentos para selecionar tabela de destino e para obter os valores de coluna a serem usados. Retorne um URI de conteúdo para o linha recém-inserida.
-
update()
- Atualize as linhas existentes no seu provedor. Usar os argumentos para selecionar a tabela e as linhas para atualizar e obter os valores de coluna atualizados. Retorne o número de linhas atualizadas.
-
delete()
- Exclua linhas do provedor. Use os argumentos para selecionar a tabela e as linhas para excluir. Retorne o número de linhas excluídas.
-
getType()
- Retorne o tipo MIME correspondente a um URI de conteúdo. Esse método é descrito em mais detalhes na seção Implementar tipos MIME do provedor de conteúdo.
-
onCreate()
-
Inicialize seu provedor. O sistema Android chama esse método imediatamente após
cria seu provedor. Seu provedor não é criado até que um
O objeto
ContentResolver
tenta acessá-lo.
Esses métodos têm a mesma assinatura do método
ContentResolver
.
A implementação desses métodos precisa considerar o seguinte:
-
Todos esses métodos, exceto
onCreate()
podem ser chamados por várias linhas de execução de uma só vez, portanto, precisam ser thread-safe. Para saber mais sobre várias linhas de execução, consulte a Visão geral dos processos e linhas de execução. -
Evite realizar operações demoradas no
onCreate()
. Adie tarefas de inicialização até que sejam realmente necessárias. A seção sobre como implementar o método onCreate() discute isso em mais detalhes. -
Embora seja necessário implementar esses métodos, o código não precisa fazer nada além de
retornar o tipo de dados esperado. Por exemplo, é possível impedir que outros aplicativos
de inserir dados em algumas tabelas, ignorando a chamada
insert()
e retornantes 0.
Implementar o método query()
A
O método ContentProvider.query()
precisa retornar um objeto Cursor
ou, se
falhar, gere uma Exception
. Se você estiver usando um banco de dados SQLite como dados
armazenamento, é possível retornar o Cursor
retornado por um dos
query()
da classe SQLiteDatabase
.
Se a consulta não corresponder a nenhuma linha, retornará Cursor
.
instância em que o método getCount()
retorna 0.
Retorne null
somente se tiver ocorrido um erro interno durante o processo de consulta.
Se você não estiver usando um banco de dados SQLite como armazenamento de dados, use uma das subclasses concretas
de Cursor
. Por exemplo, a classe MatrixCursor
implementa um cursor em que cada linha é uma matriz de instâncias de Object
. Com essa classe,
use addRow()
para adicionar uma nova linha.
O sistema Android precisa ser capaz de comunicar o Exception
.
além dos limites do processo. O Android pode fazer isso para as seguintes exceções que são úteis
no tratamento de erros de consulta:
-
IllegalArgumentException
. Isso pode ser exibido se o provedor recebe um URI de conteúdo inválido. -
NullPointerException
Implementar o método insert()
O método insert()
adiciona uma
nova linha à tabela adequada, usando os valores no campo ContentValues
. Se um nome de coluna não estiver no argumento ContentValues
,
pode querer fornecer um valor padrão para ele no código do provedor ou no banco de dados
esquema.
Esse método retorna o URI de conteúdo da nova linha. Para construir isso, acrescente o novo
da chave primária da linha, geralmente o valor _ID
, ao URI de conteúdo da tabela, usando
withAppendedId()
.
Implementar o método delete()
Método delete()
não precisa excluir linhas do seu armazenamento de dados. Se você estiver usando um adaptador de sincronização
com seu provedor, marque uma linha excluída
com uma opção "excluir" em vez de remover a linha completamente. O adaptador de sincronização pode
verifica se há linhas excluídas e as remove do servidor antes de excluí-las do provedor.
Implementar o método update()
O método update()
usa o mesmo argumento ContentValues
usado pelo
insert()
e o
mesmos argumentos selection
e selectionArgs
usados por
delete()
e
ContentProvider.query()
.
Isso pode permitir que você reutilize código entre esses métodos.
Implementar o método onCreate()
O sistema Android chama onCreate()
.
quando iniciar o provedor. Realize apenas inicialização de execução rápida
tarefas nesse método e adiar a criação do banco de dados e o carregamento de dados até que o provedor
recebe uma solicitação pelos dados. Se você realizar tarefas longas
onCreate()
, você torna o
do provedor. Isso, por sua vez, desacelera a resposta do provedor a outros
aplicativos conteinerizados.
Os dois snippets a seguir demonstram a interação entre
ContentProvider.onCreate()
e
Room.databaseBuilder()
. A primeira
snippet mostra a implementação de
ContentProvider.onCreate()
, em que o
O objeto de banco de dados é criado, e os identificadores dos objetos de acesso a dados são criados:
Kotlin
// Defines the database name private const val DBNAME = "mydb" ... class ExampleProvider : ContentProvider() { // Defines a handle to the Room database private lateinit var appDatabase: AppDatabase // Defines a Data Access Object to perform the database operations private var userDao: UserDao? = null override fun onCreate(): Boolean { // Creates a new database object appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build() // Gets a Data Access Object to perform the database operations userDao = appDatabase.userDao return true } ... // Implements the provider's insert method override fun insert(uri: Uri, values: ContentValues?): Uri? { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Java
public class ExampleProvider extends ContentProvider // Defines a handle to the Room database private AppDatabase appDatabase; // Defines a Data Access Object to perform the database operations private UserDao userDao; // Defines the database name private static final String DBNAME = "mydb"; public boolean onCreate() { // Creates a new database object appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build(); // Gets a Data Access Object to perform the database operations userDao = appDatabase.getUserDao(); return true; } ... // Implements the provider's insert method public Cursor insert(Uri uri, ContentValues values) { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Implementar tipos MIME do ContentProvider
A classe ContentProvider
tem dois métodos para retornar tipos MIME:
-
getType()
- Um dos métodos obrigatórios que devem ser implementados em qualquer provedor.
-
getStreamTypes()
- Um método que deverá ser implementado se o provedor oferecer arquivos.
Tipos MIME para tabelas
O método getType()
retorna uma
String
no formato MIME que descreve o tipo de dados retornados pelo conteúdo.
URI. O argumento Uri
pode ser um padrão em vez de um URI específico.
Nesse caso, retorne o tipo de dados associado aos URIs de conteúdo que correspondem ao
padrão
Para tipos comuns de dados, como texto, HTML ou JPEG,
getType()
retorna o valor padrão.
tipo MIME para esses dados. Uma lista completa desses tipos de padrões está disponível na
Tipos de mídia MIME IANA
site.
Para URIs de conteúdo que apontam para uma linha ou linhas de dados de tabela,
Devolução por getType()
um tipo MIME no formato MIME específico do fornecedor do Android:
-
Parte do tipo:
vnd
-
Parte do subtipo:
-
Se o padrão de URI for para uma única linha:
android.cursor.item/
-
Se o padrão de URI for para mais de uma linha:
android.cursor.dir/
-
Se o padrão de URI for para uma única linha:
-
Parte específica do provedor:
vnd.<name>
.<type>
Forneça
<name>
e<type>
. O valor<name>
é globalmente exclusivo, e o valor<type>
é exclusivo para o URI correspondente padrão Uma boa opção para<name>
é o nome da sua empresa ou parte do nome do pacote do Android do seu aplicativo. Uma boa escolha para o<type>
é uma string que identifica a tabela associada ao URI.
Por exemplo, se a autoridade de um provedor
com.example.app.provider
e expõe uma tabela chamada
table1
, o tipo MIME de várias linhas em table1
será:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Para uma única linha de table1
, o tipo MIME será:
vnd.android.cursor.item/vnd.com.example.provider.table1
Tipos MIME para arquivos
Caso seu provedor ofereça arquivos, implemente
getStreamTypes()
:
O método retorna uma matriz String
de tipos MIME para os arquivos do provedor.
para uma determinada URI de conteúdo. Filtrar os tipos MIME oferecidos pelo tipo MIME
filter, para que retorne somente os tipos MIME que o cliente quer tratar.
Por exemplo, considere um provedor que oferece imagens de fotos como arquivos em JPG,
PNG e GIF.
Se um aplicativo chamar ContentResolver.getStreamTypes()
com a string de filtro image/*
, para algo que
é uma "imagem",
o método ContentProvider.getStreamTypes()
vai retornar a matriz:
{ "image/jpeg", "image/png", "image/gif"}
Se o aplicativo só estiver interessado em arquivos JPG, então ele poderá chamar
ContentResolver.getStreamTypes()
pela string de filtro *\/jpeg
;
getStreamTypes()
retorna:
{"image/jpeg"}
Caso seu provedor não ofereça nenhum dos tipos MIME solicitados na string de filtro,
getStreamTypes()
retorna null
.
Implementar uma classe de contrato
Uma classe de contrato é uma classe public final
que contém definições de constantes para o
URIs, nomes de coluna, tipos MIME e outros metadados que pertencem ao provedor. A classe
estabelece um contrato entre o provedor e outros aplicativos, garantindo que o provedor
podem ser acessados corretamente mesmo se houver mudanças nos valores reais de URIs, nomes de colunas
e assim por diante.
Uma classe de contrato também ajuda os desenvolvedores porque geralmente tem nomes mnemônicos para as constantes, portanto, é menos provável que os desenvolvedores usem valores incorretos para nomes de colunas ou URIs. Por se tratar de um , ela pode conter documentação Javadoc. Ambientes de desenvolvimento integrados, como O Android Studio pode preencher automaticamente nomes de constantes da classe de contrato e exibir o Javadoc para as constantes.
Os desenvolvedores não podem acessar o arquivo de classe da classe de contrato do seu aplicativo, mas podem compilá-lo estaticamente no aplicativo a partir de um arquivo JAR que você fornecer.
A classe ContactsContract
e as classes aninhadas dela são exemplos de
classes de contrato.
Implementar permissões do provedor de conteúdo
As permissões e o acesso, para todos os aspectos do sistema Android, estão descritos detalhadamente em Dicas de segurança. A Visão geral do armazenamento de dados e arquivos também descreve a segurança e as permissões em vigor para vários tipos de armazenamento. Em resumo, os pontos importantes são os seguintes:
- Por padrão, os arquivos de dados armazenados no armazenamento interno do dispositivo são particulares para sua aplicativo e provedor.
-
SQLiteDatabase
bancos de dados que você criar são particulares para sua aplicativo e provedor. - Por padrão, os arquivos de dados salvos no armazenamento externo são públicos e legível para o mundo. Não é possível usar um provedor de conteúdo para restringir o acesso a arquivos em armazenamento externo, pois outros aplicativos podem usar outras chamadas de API para lê-los e gravá-los.
- O método chama para abrir ou criar arquivos ou bancos de dados SQLite no sistema interno do seu dispositivo armazenamento pode dar acesso de leitura e gravação a todos os outros aplicativos. Se você usa um arquivo ou banco de dados interno como repositório do seu provedor e fornece a ele "legível para todos" ou "gravável globalmente", as permissões definidas para o provedor no o manifesto não protegem seus dados. O acesso padrão para arquivos e bancos de dados em o armazenamento interno é "privado"; não mude isso no repositório do seu provedor.
Se você quiser usar as permissões do provedor de conteúdo para controlar o acesso aos seus dados, faça o seguinte: armazenar seus dados em arquivos internos, bancos de dados SQLite ou na nuvem, como em um servidor remoto e manter os arquivos e bancos de dados privados do seu aplicativo.
Implementar permissões
Por padrão, todos os aplicativos podem ler ou gravar no seu provedor, mesmo se os dados subjacentes forem
private porque, por padrão, seu provedor não tem permissões definidas. Para mudar isso,
defina permissões para o provedor no arquivo de manifesto, usando atributos ou filhos
elementos do elemento
<provider>
. É possível definir permissões que se aplicam a todo o provedor,
a certas tabelas, registros ou todos os três.
Você define permissões para seu provedor com um ou mais
<permission>
no arquivo de manifesto. Para tornar
permissão exclusiva do seu provedor, use escopo estilo Java para o
android:name
. Por exemplo, nomeie a permissão de leitura
com.example.app.provider.permission.READ_PROVIDER
:
A lista a seguir descreve o escopo das permissões do provedor, começando com o permissões que se aplicam a todo o provedor e tornando-se mais refinado. Permissões mais específicas têm precedência sobre as com escopo maior.
- Única permissão de leitura e gravação no nível do provedor
-
Permissão que controla o acesso de leitura e gravação a todo o provedor, especificada
com o atributo
android:permission
do Elemento<provider>
. - Permissões de leitura e gravação separadas no nível do provedor
-
Uma permissão de leitura e uma permissão de gravação para todo o provedor. Você as especifica
com
android:readPermission
eandroid:writePermission
do Elemento<provider>
. Elas têm precedência sobre a permissão exigida peloandroid:permission
: - Permissão no nível do caminho
-
Permissão de leitura, gravação ou leitura/gravação para um URI de conteúdo no provedor. Você especifica
para cada URI que você quer controlar com um
Elemento filho
<path-permission>
do<provider>
. Para cada URI de conteúdo especificado, é possível especificar um permissão de leitura/gravação, permissão de leitura, permissão de gravação ou todas as três. Os recursos de leitura as permissões de gravação têm precedência sobre a permissão de leitura/gravação. Além disso, no nível do caminho tem precedência sobre as permissões no nível do provedor. - Permissão temporária
-
um nível de permissão que concede acesso temporário a um aplicativo, mesmo se ele
não tem as permissões que normalmente são necessárias. O
de acesso reduz o número de permissões que um aplicativo precisa solicitar
seu manifesto. Quando você ativa permissões temporárias, os únicos aplicativos que precisam
as permissões permanentes do provedor são aquelas que acessam continuamente
seus dados.
Por exemplo, considere as permissões necessárias se você estiver implementando um provedor de e-mail e um app e querem permitir que um aplicativo visualizador de imagens externos exiba anexos de fotos do seu de nuvem. Para dar ao visualizador de imagens o acesso necessário sem as permissões, você pode configurar permissões temporárias de URIs de conteúdo para fotos.
Projete seu app de e-mail para que que, quando o usuário deseja exibir uma foto, o aplicativo envia uma intent contendo o URI de conteúdo da foto e sinalizações de permissão para o visualizador de imagens. O visualizador de imagens pode consultar o provedor de e-mail para recuperar a foto, mesmo que o visualizador não a permissão normal de leitura para seu provedor.
Para ativar permissões temporárias, defina o
android:grantUriPermissions
do atributo<provider>
elemento ou adicione um ou mais<grant-uri-permission>
elementos filhos à sua<provider>
. LigaçãoContext.revokeUriPermission()
sempre que você remover o suporte a um URI de conteúdo associado a uma permissão temporária do seu de nuvem.O valor do atributo determina quanto do seu provedor fica acessível. Se o atributo for definido como
"true"
, o sistema concederá permissões permissão para todo o provedor, substituindo quaisquer outras permissões necessárias com base nas permissões no nível do provedor ou do caminho.Se essa sinalização estiver definida como
"false"
, adicione<grant-uri-permission>
elementos filhos à sua Elemento<provider>
. Cada elemento filho especifica o URI de conteúdo ou URIs para os quais o acesso temporário é concedido.Para delegar acesso temporário a um aplicativo, uma intent precisa conter a sinalização
FLAG_GRANT_READ_URI_PERMISSION
, aFLAG_GRANT_WRITE_URI_PERMISSION
ou ambas. Esses são definidas com o métodosetFlags()
.Se o atributo
android:grantUriPermissions
não estiver presente, presume-se que ele seja"false"
.
O <provedor> elemento
Assim como os componentes Activity
e Service
,
uma subclasse de ContentProvider
;
é definido no arquivo de manifesto do aplicativo, usando o
<provider>
. O sistema Android recebe as seguintes informações
o elemento:
-
Autoridade
(
android:authorities
) - Nomes simbólicos que identificam todo o provedor dentro do sistema. Isso atributo é descrito em mais detalhes nas Seção Projetar URIs de conteúdo.
-
Nome da classe do provedor
(
android:name
) -
A classe que implementa
ContentProvider
. Esta aula é descritos em mais detalhes nos Implemente a seção da classe ContentProvider. - Permissões
-
atributos que especificam as permissões que outros aplicativos precisam ter para acessar
dados do provedor:
-
android:grantUriPermissions
: flag de permissão temporária -
android:permission
: permissão única de leitura/gravação em todo o provedor -
android:readPermission
: permissão de leitura em todo o provedor. -
android:writePermission
: permissão de gravação em todo o provedor.
As permissões e seus atributos correspondentes são descritos em mais nos detalhes Seção Implementar permissões do provedor de conteúdo.
-
- Atributos de início e controle
-
Esses atributos determinam como e quando o sistema Android inicia o provedor, o
características de processo do provedor e outras configurações do ambiente de execução:
-
android:enabled
: flag que permite ao sistema iniciar o provedor. -
android:exported
: flag que permite que outros aplicativos usem esse provedor -
android:initOrder
: a ordem em que esse provedor é iniciado. em relação a outros provedores no mesmo processo -
android:multiProcess
: flag que permite ao sistema iniciar o provedor. no mesmo processo que o cliente que faz a chamada -
android:process
: o nome do processo em que o provedor é executado. -
android:syncable
: flag que indica que os dados do provedor precisam ser sincronizadas com os dados em um servidor
Esses atributos estão totalmente documentados no guia para as
<provider>
. -
- Atributos informacionais
-
Ícone e rótulo opcionais para o provedor:
-
android:icon
: um recurso drawable que contém um ícone para o provedor. O ícone aparece ao lado do marcador do provedor na lista de apps no Configurações > Apps > Todos. -
android:label
: um rótulo informativo que descreve o provedor, o dados ou ambos. O marcador aparece na lista de apps em Configurações > Apps > Todos.
Esses atributos estão totalmente documentados no guia para as
<provider>
. -
Observação:caso seu app seja destinado ao Android 11 ou versões mais recentes, confira o Documentação de visibilidade do pacote para outras necessidades de configuração.
Intents e acesso a dados
Os aplicativos podem acessar um provedor de conteúdo indiretamente com uma Intent
.
O aplicativo não chama nenhum dos métodos de ContentResolver
ou
ContentProvider
. Em vez disso, ele envia uma intent que inicia uma atividade,
que geralmente faz parte do aplicativo do provedor. A atividade de destino é responsável por
recuperação e exibição dos dados na interface.
Dependendo da ação na intent, a atividade de destino também pode solicitar que o usuário faça modificações nos dados do provedor. Uma intent também pode conter “extras” dados que a atividade de destino exibe na interface. O usuário tem a opção de alterar esses dados antes de usá-los para modificar o dados no provedor.
O acesso via intents pode ser usado para ajudar na integridade dos dados. Seu provedor pode depender sobre como inserir, atualizar e excluir dados de acordo com uma lógica de negócios estritamente definida. Se permitir que outros aplicativos modifiquem seus dados diretamente pode levar a dados inválidos.
Se você quiser que os desenvolvedores usem o acesso via intents, certifique-se de documentá-lo minuciosamente. Explicar por que o acesso via intents pela IU do aplicativo é melhor do que tentar e modificar os dados com o código.
O gerenciamento de uma intent recebida que quer modificar os dados do seu provedor não é diferente de o processamento de outras intents. Para saber mais sobre o uso de intents, leia Intents e filtros de intent.
Para mais informações relacionadas, consulte a Visão geral do provedor de agenda.