Caso prefira, você encontrará todo esse material, em inglês, no site do Developer Android. A tradução e comentários dos materiais eu faço livremente para ajudar a comunidade que fala português.

segunda-feira, 11 de abril de 2011

Content Providers - Parte 01

Content Providers guardam e buscam dados e os fazem disponíveis para todas as aplicações. Eles são a única maneira de se compartilhar dados entre aplicações; não existem um local comum para se guardar dados e que todos os pacotes Android podem acessar.

O Android vem com um número de content providers para tipos de dados comuns (audio, video, imagens, informação de contatos, e assim por diante). Você pode ter uma noção de quais são eles no pacote android.provider. Você pode buscar dados nesses providers pelos dados que eles contém (apesar de, para alguns, você ter de prover as permissões para ler tais dados).

Se você quer fazer de seus dados algo público, você tem duas opções: você pode criar seu próprio content provider (uma subclasse de ContentProvider) ou você pode adicionar dados a um provider existente - se existe um que controla o mesmo tipo de dados e que você tenha permissão de acessar.

O básico de Content Provider

Como um content provider guarda os dados é algo que diz respeito ao designer da aplicação. Mas todos os content providers implementam uma interface comum para fazer buscas e retornar resultados - assim como adicionar, alterar e excluir dados.

É uma interface que os clientes usarão indiretamente, mas geralmente através de objetos do tipo ContentResolver. Você consegue um ContentResolver chamando getContentResolver() a partir da implementação de uma atividade ou de outro componente da aplicação.

ContentResolver cr = getContentResolver();
Você pode então usar os métodos do ContentResolver para interagir com quaisquer content providers que você esteja interessado.

Quando uma pesquisa é iniciada, o sistema Android identifica o content provider que é o alvo para a pesquisa e se certifica que ela esteja rodando. O sistema instancia todos os objetos ContentProvider; você nunca precisará fazê-lo. Na verdade, você nunca interage com os objetos ContentProvider. Tipicamente, existe apenas uma única instância de cada tipo de ContentProvider. Mas eles podem se comunicar com multiplos objetos ContentResolver em diferentes aplicações e processos. Os interação entre processos é gerenciada pelas classes ContentResolver e ContentProvider.

O modelo de dados

Content providers expoem seus dados como uma tabela simples de um banco de dados, onde cada linha é um registro e cada coluna são os dados de um tipo particular.

_ID NUMBER NUMBER_KEY LABEL NAME TYPE
13 (425) 555 6677 425 555 6677 Kirkland office Bully Pulpit TYPE_WORK
44 (212) 555-1234 212 555 1234 NY apartment Alan Vain TYPE_HOME
45 (212) 555-6657 212 555 6657 Downtown office Alan Vain TYPE_MOBILE
53 201.555.4433 201 555 4433 Love Nest Rex Cars TYPE_HOME


Cada registro inclui um campo _ID numérico que identifica unicamente o registro dentro da tabela. IDs podem ser usados para correlacionar registros em tabelas relacionadas - por exemplo, para encontrar o telefone de uma pessoa em uma tabela e a foto dela em outra tabela.

Uma pesquisa retorna um objeto de cursor que pode se mover de registro a registro e de coluna para coluna e ler os conteúdos de cada campo. Ele tem métodos especializados para ler cada tipo de dados. Então, para ler um campo, você deve saber o tipo de dados que o campo contém.

URIs - Uniform Resource Identifier

Cada content provider expõe uma URI pública (envelopada como um objeto URI) que unicamente identifica seu data set. Um content provider que controla multiplos data sets (multiplas tabelas) expõem uma URI separada para cada um. Todas as URIs para providers começam com a string "content://". O esquema content: identifica os dados como sendo controlados pelo content provider.

Se você está definindo um content provider, é uma boa ideia também definir uma constante para sua URI, para simplificar o código do cliente e fazer dos futuros updates algo mais limpo. O Android define constantes CONTENT_URI para todos os provedores que vem com sua plataforma. Por exemplo, a URI para a tabela que tem os números dos contatos do smartphone e a URI para a tabela que guarda as fotos das pessoas (ambas controladas pelo content provider de contatos) são:
android.provider.Contacts.Phones.CONTENT_URI
android.provider.Contacts.Photos.CONTENT_URI

A constante URI é usada em todas as interações com o content provider. Cada método ContentResolver toma a URI como seu primeiro argumento. É o que identifica que provider o ContentResolver deverá falar primeiro e em qual tabela o provider acessará.

Fazendo uma pesquisa em um Content Provider

Você precisa de três peças de informação para fazer uma pesquisa em um content provider:
  • A URI que identifica o provider
  • Os nomes dos campos que você quer receber
  • Os tipos de dados para esses campos

Se você está fazendo uma pesquisa por um registro em particular, deve também saber o ID para este registro.


Criando a pesquisa


Para fazer uma pesquisa em um content provider, você pode usar ou o ContentResolver.query() ou o Activity.managerQuery(). Ambos recebem os mesmos argumentos e ambos retornam um objeto do tipo Cursos. Contudo, managedQuery() faz com que a atividade gerencie o ciclo de vida do Cursos. Um Cursor gerenciado tem funções mais completas, como fazer o descarregamento de si mesmo quando a atividade é pausada e fazendo novamente a pesquisa quando a atividade se reinicia. Você pode solicitar que uma atividade gerencie um Cursos não gerenciado chamando Activity.startManagingCursor().

O primeiro argumento para query() ou managedQuery() é a URI do provider - A constante CONTENT_URI que identifica um ContentProvider particular e seu data set.


Para restringir a pesquisa a apenas um registro, você pode incluir o valor para _ID para o registro na URI - ou seja, colocando o ID do registro como último segmento no caminho da URI. Por exemplo, se o ID é 23, a URI deverá ser:
content:// . . . .//23
Existem algums métodos de ajuda, particularmente o ContentUris.withAppendedId() e Uri.withAppendedPath(), que faz ser mais fácil adicionar um ID para a URI. Ambos são métodos estáticos que retornam um objeto URI com o ID adicionado. Então, por exemplo, se você está fazendo uma pesquisa pelo registro 23 no banco de dados dos contatos, você deverá construir uma query como mostrado:

import android.provider.Contacts.People;
import android.content.ContentUris;
import android.net.Uri;
import android.database.Cursor;


// Use the ContentUris method to produce the base URI for the contact with _ID == 23.
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);


// Alternatively, use the Uri method to produce the base URI.
// It takes a string rather than an integer.
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");


// Then query for this specific record:
Cursor cur = managedQuery(myPerson, null, null, null, null);


Os outros argumentos para query() e managedQuery() delimitam a query em maiores detalhes. Eles são:

  • Os nomes das colunas que devem ser retornadas. Um valor null retorna todas as colunas. De outra maneira, apenas as colunas que são listadas por nome são retornadas. Todos os content providers que vem com a plataforma definem constatnes para suas colunas. Por exemplo, a classe android.provider.Contacts.Phones definem constantes para os nomes das colunas em uma tabela de telefones onde há os campos _ID, NUMBER, NUMBER_KEY, NAME e assim por diante. 
  • Um filtro detalhando que linhas a retornar, formatado como numa cláusula Where do SQL (excluindo o Where). Um valor null retorna todas as linhas (a não ser que a query limite o retorno a um registro).
  • Seleção de argumentos
  • Ordenação das linhas que são retornadas, formatadas como numa cláusula Order By do SQL (excluindo o Order By). Um valor null retorna os registros na order padrão da tabela, que poderá estar desordenada.

Vamos ver um exemplo de query para recuperar uma lista de contatos e seus números de telefone.

import android.provider.Contacts.People;
import android.database.Cursor;

// Form an array specifying which columns to return.
String[] projection = new String[] {
People._ID,
People._COUNT,
People.NAME,
People.NUMBER
};


// Get the base URI for the People table in the Contacts content provider.
Uri contacts = People.CONTENT_URI;


// Make the query.
Cursor managedCursor = managedQuery(contacts,
projection, // Which columns to return
null, // Which rows to return (all rows)
null, // Selection arguments (none)
// Put the results in ascending order by name
People.NAME + " ASC");


Essa query recupera dados da tabela de pessoas do content provider Contacts. Ele pega o nome, número de telefone e o ID único para cada contato. Ele também reporta o número de registros que são retornados com o campo _COUNT de cada registro.


As constantes para os nomes das colunas são definidos em interfaces variadas - _ID e _COUNT dentro da BaseColumns, NAME dentro da PeopleColumns e NUMBER dentro de PhoneColumns. A classe Contacts.People implementam cada uma dessas interfaces que é a razão do código acima se referir a eles usando apenas o nome de classe.


O que é retornado por uma pesquisa


Uma pesquisa retorna um conjunto de zero ou mais registros do banco de dados. Os nomes das colunas, a ordem padrão e seus tipos de dados são específicos de cada content provider. Mas cara provider tem uma coluna _ID, que guarda um número único para cada registro. Cada provider pode também reportar um número de registros na columa _COUNT; seu valor é o mesmo para todas as linhas.


_ID _COUNT NAME NUMBER
44 3 Alan Vain 212 555 1234
13 3 Bully Pulpit 425 555 6677
53 3 Rex Cars 201 555 4433


Os dados retornados são expostos por um objeto do tipo Cursor que pode ser usado para que se faça a iteração para frente e para trás nos resultados. Você pode usar esse objeto apenas para ler os dados. Para adicionar, modificar ou excluir dados, você deve usar um objeto ContentResolver.


Lendo os dados retornados


O objeto cursor retornado pela pesquisa prove acesso ao recordset de resultados. Se você tem dados pesquisados por um registro específico esse conjunto terá apenas um único registro. Se não for pesquisado por um ID, ele retornará múltiplos valores (e se não há registros que se encaixem na pesquisa, ele virá vazio). Você pode ler dados de campos específicos no registros mas você deve saber o tipo de dados para o campo, já que o objeto Cursor tem um método separado para ler cada tipo de dados - como getString(), getInt(), getFloat(). (Para a maioria dos casos, se você chamar o método de leitura de strings, o objeto Cursor retornará a representação dos dados em forma de string). O Cursor deixa que você requeira o nome de coluna a partir de um indice da coluna ou o número de índice do nome da coluna.


O código abaixo mostra como ler os nomes e números de telefones da query ilustrada acima.

import android.provider.Contacts.People;


private void getColumnData(Cursor cur){
if (cur.moveToFirst()) {
String name;
String phoneNumber;
int nameColumn = cur.getColumnIndex(People.NAME);
int phoneColumn = cur.getColumnIndex(People.NUMBER);
String imagePath;


do {
// Get the field values
name = cur.getString(nameColumn);
phoneNumber = cur.getString(phoneColumn);


// Do something with the values.
...
} while (cur.moveToNext());
}
}
Se a query retornar dados binários, como uma imagem ou som, você poderá recuperar os dados usando Cursor.getBlob(). O retorno é um array de bytes.

1 comentários:

Afiando meu machado disse...

Excelente post

Gostaria de saber se essa forma de manipulação de conteúdo continua válida para o Android 4?

Da mesma forma posso trabalhar com webservices?

Grato

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
Related Posts Plugin for WordPress, Blogger...