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.

sábado, 16 de abril de 2011

0 comentários

Intents e Intent Filters - Parte 02

Resolução do Intent

Intents podem ser divididos em dois grupos:
  • Intents explicitos designam o componente alvo pelo nome. Já que os nomes dos componentes normalmente não são conhecidos pelos desenvolvedores de outras aplicações, intents explícitos são tipicamente usados para mensagens internas à aplicação - como uma atividade iniciando um serviço subordinado ou lançando uma atividade irmã.
  • Intents implicitos não nomeiam um alvo. Intents implicitos são normalmente usados para ativar componentes em outras aplicações.
O Android entrega um intent explicito para uma instância de uma classe alvo designada. Nada no objeto intent que não o nome importa para determinar que componente deve usar o intent.

Uma estratégia diferente é necessária para intents implicitos. Na ausência de um alvo determinad, o sistema Android deve encontrar o melhor componente (ou componentes) para manusear o intent - uma atividade única ou serviço que realize uma ação de requisição ou um conjunto de broadcast receivers para responder ao anúncio do broadcast. Ele encontra o melhor componente comparando o conteúdo do objeto Intent nos intent filters, estruturas associadas com componentes que podem potencialmente receber intents. Filtros anunciam as capacidades de um componente e delimitam os intents que podem manuseá-lo. Eles abrem o componente para a possibilidade de receber intents implicitos de um tipo anunciado. Se um componente não tem quaisquer intent filtes, ele pode receber apenas intents explícitos. Um componente com filtros podem receber ambos tipos de intents.

Apenas três aspectos de um objeto Intent são consultados quando o objeto é testado contra um intent filter:
action
dados (ambos URI e tipo de dados)
category

Intent filters

Para informar ao sistema quais intents implicitos podem ser manuseados, atividades, serviços e broadcast receivers podem ter um ou mais intent filters. Cada filtro descreve a capacidade do componente ou um conjunto de intents que o componente poderá receber. Isso, em efeito, filtra intents de um tipo desejado enquantro filtra intents não requeridos - mas apenas intents implicitos não requeridos. Um intent explícito é sempre entregue para seu algo, não importa o que ele contenha: o filtro simplesmente não é consultado. Mas um intent implícito é entregue para um componente apenas se ele pode passar por algum dos filtros do compente que se quer que seja o alvo.

Um componente tem filtros separados para cada trabalho que ele pode fazer.

Um intent filter é uma instância da classe IntentFilter. Contudo, já que o sistema Android deve saber sobre as capacidades de um componente antes que possa lançá-lo, intent filters são geralmente configurados no manifesto da aplicação. Uma exceção seriam filtros para broadcast receivers que podem ser registrados dinamicamente chamando Context.registerReceiver().

Um filtro tem campos que são paralelos à ação, dados e categoria de um objeto Intent. Um intent implicito é testado contra o filtro em todas essas áreas. Para ser entregue para o componente que é dono do filtro, ele deve passar em todos os testes. Se ele falha em apenas um, o sistema Android não fará a entrega deste ao componente. Contudo, caso o componente tenha múltiplos intent filters, um intent que não passou poderá passar através de outro intent filter.

Os três testes que são feitos são:

Action test
Category test
Data test

sexta-feira, 15 de abril de 2011

0 comentários

Intents e Intent Filters - Parte 01

Três dos principais componentes de uma aplicação - atividades, serviços e broadcast receivers - são ativados através de mensagens, chamadas intents. Mensagens intent são um facilitador para rodar bindings entre componentes da mesma ou de diferentes aplicações. O intent, um objeto Intent, é uma estrutura de dados passiva que comporta uma descrição abstrata da operação a ser realizada -ou, nos casos de broadcasts, uma descrição de algo que aconteceu e que é anunciado. Existem mecanismos separados para entregar intents para cada tipo de componente:
  • Um objeto intent é passado para Context.startActivity() ou Activity.startActivityForResult() para lançar uma atividade ou receber uma atividade existente para fazer algo novo (ele pode também ser passado por Activity.setResult() para retornar informações para a atividade que chamou o startActivityForResult().
  • Um objeto Intent é passado para Context.startService() para iniciar um serviço ou entregar novas instruções para um serviço de saída. Similarmente, um intent pode ser passado para Context.bindService() para estabelecer uma conexão entre o componente que chama e o serviço alvo. Ele pode opcionalmente iniciar o serviço se ele já não estiver rodando.
  • Objetos intent passados para qualquer método broadcast (como Context.sendBroadcast(), Context.sendOrderedBroadcast() ou Context.sendStickyBroadcast()) são entregues para todos os broadcast receivers interessados. Muitos tipos de broadcasts originam-se em código de sistema.
Em cada caso, o sistema Android encontra a atividade apropriada, serviço ou broadcast receiver para responder ao intent, instanciando-os se necessário. Não existe problema nessas mensagens de sistema: broadcast intents são entregues apenas para broadcast receivers, nunca para atividades ou serviços. Um intent passado para startActivity() é entregue apenas para uma atividade, nunca para um serviço ou broadcast receiver e assim por diante.

Objetos Intent

Um objeto intent é um conjunto de informações. Ele contém informações de interesse para o componente que o recebe (como uma ação a ser tomada e os dados de argumento) mais informações de interesse para o sistema Android (como qual categoria de componente deve manusear o intent e instruções em como lançar a atividade alvo). Mas, ele deve conter principalmente:

Nome de Componente

O nome do componente que deve manusear o intent. Esse campo é o objeto ComponentName - uma combinação de um nome completamente qualificado do componente alvo e o nome do pacote setado no arquivo de manifesto da aplicação onde o componente reside.

O nome de componente é opcional. Se ele é configurado, o objeto de Intent é entregue para uma instância da classe designada. Se ele não é configurado, o Android usa outra informação do objeto Intent para localizar um alvo que possa recebê-lo.

O nome do componente pode ser configurado por setComponent(), setClass(), setClassName() e lido por getComponent().

Ação

Uma string nomeando a ação a ser realizada -ou, no caso de broadcast intents, a ação que está sendo executada e reportada. A classe intent define um número de constantes de ação, incluindo esses:
ConstantTarget componentAction
ACTION_CALLactivityInicia uma chamada de telefone
ACTION_EDITactivityMostra dados para o usuário editar
ACTION_MAINactivityInicia a atividade que está marcada como inicial em modo vazio e com nenhum retorno
ACTION_SYNCactivitySincroniza dados no servidor a partir do dispositivo móvel
ACTION_BATTERY_LOWbroadcast receiverMostra um aviso que a bateria está baixa
ACTION_HEADSET_PLUGbroadcast receiverMostra que um fone de ouvido foi plugado no dispositivo ou desplugado
ACTION_SCREEN_ONbroadcast receiverA tela foi ligada
ACTION_TIMEZONE_CHANGEDbroadcast receiverAs configurações da zona de tempo foi mudada
Data

A URI dos dados a serem capturados e o MIME type para eles.

Category

Uma string contendo informações adicionados sobre o tipo de componentes que devem ser manuseados pelo intent.


ConstantMeaning
CATEGORY_BROWSABLEA atividade alvo pode ser chamada de maneira segura pelo navegador para mostrar dados referenciados por um link (imagem ou email, etc)
CATEGORY_GADGETA atividade pode ser embarcada dentro de outra atividade que hospede gadgets.
CATEGORY_HOMEA atividade mostra a tela home, a primeira tela que o usuário vê quando o dispositivo é ligado ou quando a tela HOME é pressionada.
CATEGORY_LAUNCHERA atividade pode ser a atividade inicial ou uma tarefa e é listada no tipo da aplicação launcher.
CATEGORY_PREFERENCEA atividade alvo é o painel de preferências


Extras

Pares chave-valor com informações adicionais que devem ser entregues para o componente manuseando um intent.

Flags

Flags de tipos diversos. 

quarta-feira, 13 de abril de 2011

0 comentários

Content Providers - Parte 03

Criando um Content Provider

Para criar um content provider, você deve:

  • Configurar o sistema para guardar os dados. A maioria dos content provider guardam seus dados usando os métodos de arquivo do Android ou os bancos de dados SQLite mas você pode guardar seus dados da maneira como achar melhor. O Android provê a classe SQLiteOpenHelper para ajudá-lo a criar um banco de dados e a classe SQLiteDatabase para gerenciá-lo.
  • Extender a classe ContentProvider para prover acesso aos dados.
  • Declarar o content provider no arquivo de manifesto da sua aplicação (AndroidManifest.xml)
Extendendo a classe ContentProvider

Você define uma subclasse ContentProvider expondo seus dados para outros usando as convenções esperadas pelo ContentResolver e pelo objeto Cursor. Isso significa implementar seis métodos abstratos declarados na classe ContentProvider:
query()
insert()
update()
delete()
getType()
onCreate()
O método query() deve retornar um objeto Cursor que pode iterar pelos dados requeridos. O Cursor em si é uma interface mas o Android provê alguns objetos Cursos que você pode usar. Por exemplo, o SQLiteCursor pode interar pelos dados guardados em um banco de dados SQLite. Você obtem um objeto Cursor chamando o método query() da classe SQLiteDatabase. Existem outras implementações de cursores, como MatrixCursor, para dados não guardados em bancos de dados.

Como esses métodos ContentProvider podem ser chamados de objetos ContentProvider em diferentes provessos e threads, eles devem ser implementados como sendo do tipo thread-safe.

Como uma cortesia, você também poderá chamar ContentResolver.notifyChange() para notificar os listeners quando houverem modificações nos dados.

Ao invés de definir a subclasse você mesmo, existem outros passos que você deveria tomar para simplificar o seu trabalho de trabalhar com clientes e fazer da classe algo mais acessível:

1) Definir uma Uri como public static final e nomeada CONTENT_URI. Essa é a string que representará a URI content: completa que o content provider requer. Você deve definir uma string única para esse valor. A melhor solução é usar um nome de classe completamente qualificado do content provider (em caixa baixa). Assim, por exemplo, a URI para a classe TransportationProvider deve ser definida como mostrado aqui:

public static final Uri CONTENT_URI =
               Uri.parse("content://com.example.codelab.transportationprovider");
Se o provider tem subtabelas, também defina um CONTENT_URI para cada uma delas. Esses URIs devem ter a mesma autoridade e devem ser distintas apenas pelos caminhos. Por exemplo:
content://com.example.codelab.transportationprovider/train
content://com.example.codelab.transportationprovider/air/domestic
content://com.example.codelab.transportationprovider/air/international
2) Definir os nomes das colunas que o content provider retornará aos clientes. Se você estiver usando um banco de dados, essas colunas terão nomes idênticos aos especificados no banco de dados. Também defina como public static as constantes que os clientes podem usar nas pesquisas e outras instruções.

Esteja certo de incluir uma coluna do tipo inteiro nomeada "_id" para os IDs dos registros. Você deve ter esse campo mesmo que a tabela não contenha nenhum outro campo. Se você estiver usando o SQLite como repositório, o campo _ID deverá ser como mostrado abaixo:

INTEGER PRIMARY KEY AUTOINCREMENT

3) Cuidadosamente documente cada tipo de dados de cada coluna. O cliente necessita dessas informações para que possa ler os dados.

4) Se você está gerenciando um novo tipo de dados, você deve definir um novo tipo MIME a ser retornado em sua implementação de ContentProvider.getType(). O tipo depende em se o content: submetido ao getType() limita a requisição para um registro específico. Existe uma forma de tipo MIME para cada registro único e outro para registros múltiplos. Use os métodos Uri para ajudá-lo a determinar o que está sendo requerido.

  • Para registros simples: vnd.android.cursor.item/vnd.yourcompanyname.contenttype
    Por exemplo, uma requisição para o trem de registro 122 com a chamada abaixo
          content://com.example.transportationprovider/trains/122 
    deve retornar um MIME do tipo:
          vnd.android.cursor.item/vnd.example.rail
  • Para registros múltiplos: vnd.android.cursor.dir/vnd.yourcompanyname.contenttype
    Por exemplo, uma requisição de todos os trens como a chamada abaixo
          content://com.example.transportationprovider/trains
    deve retornar um MIME do tipo:
          vnd.android.cursor.dir/vnd.example.rail
5) Se você está expondo dados em bytes que são muito longos para serem gravados em bancos de dados - como um enorme arquivo bitmap -o campo que expõe os dados para o cliente devem conter uma string content: URI. Esse é o campo que dá ao cliente acesso aos dados do arquivo. O registro deve também ter outro campo nomeado de "_data", que lista o caminho exato do arquivo no dispositivo. Esse campo não deve poder ser lido pelo cliente mas apenas pelo ContentResolver. O cliente chamará ContentResolver.openInputStream(). O ContentResolver vai requerer o campo "_data" para o registro e já que tem permissões mais elevadas no cliente, deverá ser capaz de acessar o arquivo diretamente e retornar um wrapper de leitura do arquivo para o cliente.

Declarando um content provider

Para deixar que o sistema Android saiba dos content providers que você desenvolveu, declare-os no elemento <provider> do arquivo de manifesto. Content Providers que não são declarados no manifesto não são visíveis no sistema Android.

O atributo name é o nome da subclasse do ContentProvider. O atributo authorities é a parte de autoridade da content: URI que identifica o provedor. Por exemplo, se a subclasse ContentProvider é do tupo AutoInfoProvider, o elemento <provider> deverá se parecer como abaixo:
<provider android:name="com.example.autos.AutoInfoProvider"
          android:authorities="com.example.autos.autoinfoprovider"
          . . . />
</provider>
Note que o atributo authorities omite o caminho de parte do content: URI. Por exemplo, se as subtabelas de AutoInfoProvider são controladas por diferentes tipos de carros ou fabricantes,
content://com.example.autos.autoinfoprovider/honda
content://com.example.autos.autoinfoprovider/gm/compact
content://com.example.autos.autoinfoprovider/gm/suv
esses caminhos não serão declarados no manifesto. A authority é o que identifica o provedor, não o caminho; seu provedor pode interpretar o caminho de parte da URI da maneira como você escolher.

terça-feira, 12 de abril de 2011

0 comentários

Content Providers - Parte 02

Modificando dados
Dados mantidos por um content provider podem ser modificados das seguintes maneiras:
  • Adicionando novos registros
  • Adicionando novos valores para registros existentes
  • Fazendo updates em registros existentes
  • Excluindo registros
Todas as modificações de dados são conseguidos usando os métodos do ContentResolver. Alguns content providers requerem uma permissão mais restritiva para escrever os dados que para lê-los. Se você não tem permissão para escrever para um content provider, os métodos ContentResolver vão falhar.

Adicionando registros

Para adicionar um novo registro a um content provider, primeiro configura um mapa de pares chave-valor em um objeto ContentValues, onde cada chave bate com o nome de uma coluna do content provider e o valor é o que se deseja alterar para o novo registro nessa coluna. Então chame ContentResolver.insert() e passe a URI para o provider e o mapa de ContentValues. Esse método retorna uma URI completa do novo registro - ou seja, a URI do provedor com o ID aninhado para o novo registro. Você pode então usar essa URI para pesquisar e obter um Cursor do novo registro e para mais tarde modificar seus dados. Aqui está um exemplo:
import android.provider.Contacts.People;
import android.content.ContentResolver;
import android.content.ContentValues;


ContentValues values = new ContentValues();


// Add Abraham Lincoln to contacts and make him a favorite.
values.put(People.NAME, "Abraham Lincoln");
// 1 = the new contact is added to favorites
// 0 = the new contact is not added to favorites
values.put(People.STARRED, 1);


Uri uri = getContentResolver().insert(People.CONTENT_URI, values);

Adicionando novos valores

Uma vez que o registro existe, você pode adicionar novas informações e modificar as existentes. Por exemplo, o próximo passo para o exemplo acima seria adicionar novas informações de contato - como o nome do telefone - para uma nova entrada de dados.

A melhor maneira de adiciona um registro no banco de dados de contatos é adicionar o nome da tabela onde os novos dados deverão ser adicionados para a URI do registro e então usar essa URI para adicionar novos valores de dados. Cada tabela de contato expõe um nome para uma proposta como uma contante CONTENT_DIRECTORY. O código seguinte continua o código anterior adicionando um número de telefone e um endereço de email para o registro criado:
Uri phoneUri = null;
Uri emailUri = null;


// Add a phone number for Abraham Lincoln.  Begin with the URI for
// the new record just returned by insert(); it ends with the _ID
// of the new record, so we don't have to add the ID ourselves.
// Then append the designation for the phone table to this URI,
// and use the resulting URI to insert the phone number.
phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);


values.clear();
values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.Phones.NUMBER, "1233214567");
getContentResolver().insert(phoneUri, values);


// Now add an email address in the same way.
emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);


values.clear();()
// ContactMethods.KIND is used to distinguish different kinds of
// contact methods, such as email, IM, etc.
values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL);
values.put(People.ContactMethods.DATA, "test@example.com");
values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME);
getContentResolver().insert(emailUri, values);

Você pode colocar pequenos pedados de dados binários dentro de uma tabela chamando a versão de ContentValues.put() que recebe um array de bytes. Isso funcionaria para uma imagem de ícone ou clip de audio, por exemplo. Contudo, se você tem uma quantidade grande de dados binários a serem adicionados, como uma fotografia ou uma música completa, insira uma URI content: para os dados em uma tabela ou chamada ContentResolver.openOutputStream() com o arquivo URI (isso faz com que o content provider para guardar os dados emum arquivo e gravar o caminho deste em um campo escondido no registro).

Nesse sentido, o content provider MediaStore, o principal provider para imagens, audio e dados de vídeo, emprega uma convenção especial. O mesmo URI que é usado com query() ou managedQuery() para obter meta-information sobre os dados binários é usado com openInputStream() para obter dados de si mesmo. Similarmente, a mesma URI que é usada com o insert() para guardar meta-informações no registro do MediaStore é usado com openOutputStream() para inserir os dados por lá. O código seguinte ilustra essa convenção:
import android.provider.MediaStore.Images.Media;
import android.content.ContentValues;
import java.io.OutputStream;


// Save the name and description of an image in a ContentValues map. 
ContentValues values = new ContentValues(3);
values.put(Media.DISPLAY_NAME, "road_trip_1");
values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");
values.put(Media.MIME_TYPE, "image/jpeg");


// Add a new record without the bitmap, but with the values just set.
// insert() returns the URI of the new record.
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);


// Now get a handle to the file for that record, and save the data into it.
// Here, sourceBitmap is a Bitmap object representing the file to save to the database.
try {
    OutputStream outStream = getContentResolver().openOutputStream(uri);
    sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
    outStream.close();
} catch (Exception e) {
    Log.e(TAG, "exception while writing image", e);
}
Fazendo updates de registros em batch

Para fazer o update de registros em batch (por exemplo, para mudar a sigla "NY" para "New York" em todos os .campos), chame o método ContentResolver.update() com a coluna e valores a serem mudados.

Excluindo um registro

Para excluir um registro único, chame ContentResolver.delete() com a URI da linha específica.

Para deletar linhas múltiplas, chame ContentResolver.delete() com a URI do tipo de registro a ser excluído. Por exemplo, android.provider.Contacts.People.CONTENT_URI e uma cláusula SQL Where definindo quais linhas a serem excluídas.

segunda-feira, 11 de abril de 2011

1 comentários

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.

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...